F - True Liars POJ 1417(并查集)(DFS)

本文介绍了一个使用并查集与动态规划解决特定组合问题的方法。文章中分享了一个具体的编程实例,通过代码实现展示了如何处理复杂的数据结构和算法挑战,特别是针对字符数组越界等问题进行了详细的解释。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我的天!!做这道题发现两个重要的坑点!

1、 memset只能用于初始化数组的0和-1或者是16进制的比如0x3f3f3f3f;

2、字符数组越界了会影响后面变量的值!

好吧,搞了半天先是wa了好几发,最后超时了,过程中修复好多bug,能写出还是。。费了不少功夫,太弱了!

心累,mdzz!大哭

查了发现是要用并查集加dp做,我的天!也可能是我的DFS优化不够,先存个代码过会再看吧。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 1e4+5;
int father[maxn],Rank[maxn],sum[maxn][3],vis[maxn][2],cnt,vv[maxn][2],summ[maxn][2];

int query(int x) { //路径压缩
    if(father[x] != x) {
        int per = father[x];
        father[x] = query(father[x]);
        Rank[x] = (Rank[x] + Rank[per] + 2) %2;
    }
    return father[x];
}

void dfs(int sum1,int sum2,int i,int x,int y,int cc) { //dfs查有几种可行方案,如果只有唯一解,那么就是可行的,否则输出no
    //printf("%d-%d-%d-%d-%d-%d\n",sum1,sum2,i,x,y,cc);
    if(sum1 > x || sum2 > y)
        return ;
    if(sum1 == x && sum2 == y) {
        //printf("tt\n");
        cnt++;
        if(cnt == 1){
            for(int i = 1;i <= cc;i++) 
                vv[i][0] = vis[i][0];
        }
        return ;
    }    
    if(cnt > 1 || i > (cc)) 
        return ; 
    int t = sum1 + sum[i][0];
    int tt = sum2 + sum[i][1];
    if(!vis[i][0]) {
        vis[i][0] = 1;
        i++;
        //printf("%d-%d-%d-%d-%d\n",t,tt,i,x,y,cc);
        dfs(t,tt,i,x,y,cc); 
        i--;
        vis[i][0] = 0; // 每次检查先标记,回溯的时候再消除标记。
    }
    int j = sum1 + sum[i][1];
    int jj = sum2 + sum[i][0];
    if(!vis[i][1]) {
        //printf("%d=%d=%d=%d=%d\n",j,jj,i,x,y,cc);
        vis[i][1] = 1;
        i++;
        dfs(j,jj,i,x,y,cc);
        i--;
        vis[i][1] = 0;
    }
}

int main() { 
    //freopen("haha.txt","r",stdin);
    int m,nx,ny;
    while(~scanf("%d%d%d",&m,&nx,&ny) && (m || ny || nx)) {
        int x,y;
        if(nx == ny) {
            char str[20];
            for(int i = 1;i <= m;i++)
                scanf("%d%d%s",&x,&y,str);
            printf("no\n");
            continue;
        }
        int n = (nx + ny);
        char str[20]; //开字符数组的时候要记得开大一些
        for(int i = 0;i <= n;i++) {
            Rank[i] = 0;
            father[i] = i;
        }
        for(int i = 1;i <= m;i++) {
            scanf("%d%d%s",&x,&y,str);
            int a = query(x);
            int b = query(y);
            if(a != b) {
                father[b] = a;
                if(str[0] == 'y')
                   Rank[b] = (Rank[x] - Rank[y] + 2) % 2;
                else if(str[0] == 'n') 
                   Rank[b] = (Rank[x] - Rank[y] + 3) % 2;
            }
        }
        memset(summ,0,sizeof(summ)); //memset只能用于16进制和0或者-1
        memset(sum,0,sizeof(sum));
        memset(vis,0,sizeof(vis));
        for(int i = 1;i <= n;i++) {
            int zz = query(i);
            if(!Rank[i]) 
                summ[zz][0]++;   
            else 
                summ[zz][1]++; 
        }     
        int cc = 0;
        for(int i = 1;i <= n;i++) {
            if(summ[i][0] || summ[i][1]) {
                sum[++cc][0] = summ[i][0];
                sum[cc][1] = summ[i][1];
                sum[cc][2] = i;
            }
        }
        cnt = 0;
        dfs(0,0,1,nx,ny,cc);
        //printf("%d=",cnt);
        if(cnt == 1) {
            int nn[2005],sss = 0;
            for(int i = 1;i <= cc;i++) { //按顺序输出善良的人的编号
                if(vv[i][0]) {      
                    for(int j = 1;j <= n;j++) {
                        if(query(j) == sum[i][2] && !Rank[j]) 
                            nn[sss++] = j;                            
                    }  
                }
                else {
                    for(int j = 1;j <= n;j++) {
                        if(query(j) == sum[i][2] && Rank[j]) 
                            nn[sss++] = j;
                    }
                }
            }
            sort(nn,nn+sss);
            for(int i = 0;i < sss;i++)
                printf("%d\n",nn[i]);
            printf("end\b");
        }
        else 
            printf("no\n");
        /*for(int i = 1;i <= cc;i++)
            printf("%d-%d ",sum[i][0],sum[i][1]);
        printf("\n");*/
    }
    return 0;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值