zoukankan      html  css  js  c++  java
  • [题解]POJ_1417_(并查集背包

    https://blog.csdn.net/qq_34731703/article/details/54603652

    1.转化:yes表示双方同类,否则不同类(真的蔡

    题目变为有一些集合,内部分两个集合,现在要从每个大集合里选出一个小集合使得选出的这些集合大小之和恰好为p1,且只能有一种方案(这样才能确定每个元素是谁

    所以不用可行性dp,我们记录方案数,设$f[i][j]$为前$i$个拼成$j$的方案数,转移显然

    但是要输出方案,由于要求从小到大输出编号,而且每个小集合里有很多元素,所以不记录从哪转移,而是看$f[i-1][j-size]$方案数是否为1,如果是1的话肯定是从这转移来的,然后暴力枚举所有元素,选出属于这个集合的即可

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn=1009;
    int n,p1,p2;
    int fa[maxn],d[maxn],w[2][maxn],v[maxn],p[maxn];
    inline int find(int x){
        if(x==fa[x])return x;
        int rt=find(fa[x]);
        d[x]^=d[fa[x]];
        return fa[x]=rt;
    }
    inline void unionn(int x,int y,int k){
        int xx=find(x),yy=find(y);
        if(xx==yy)return;
        fa[xx]=yy;
        d[xx]=d[x]^d[y]^k;
    }
    int f[maxn][maxn];
    int main(){
        while(scanf("%d%d%d",&n,&p1,&p2) && (n+p1+p2)){
            for(int i=1;i<=p1+p2;i++)fa[i]=i;
            memset(d,0,sizeof(d));
            memset(v,0,sizeof(v));
            memset(w,0,sizeof(w));
            memset(f,0,sizeof(f));
            char s[10];
            for(int i=1,x,y;i<=n;i++){
                scanf("%d%d%s",&x,&y,s);
                if(s[0]=='y')unionn(x,y,0);
                else unionn(x,y,1);
            }
            //暴力找每个小集合 
            int cnt=0;
            for(int i=1;i<=p1+p2;i++)
            if(!v[i]){
                ++cnt;
                int ff=find(i);
                for(int j=i;j<=p1+p2;j++)
                if(find(j)==ff && !v[j])v[j]=1,w[d[j]][cnt]++;
                p[cnt]=ff;
            }
            //dp 
            f[0][0]=1;
            for(int i=1;i<=cnt;i++){
                int flr=min(w[0][i],w[1][i]);
                for(int j=p1;j>=flr;j--){
                    if(f[i-1][j-w[0][i]]){
                        f[i][j]+=f[i-1][j-w[0][i]];
    //                    from[i][j]=j-w[0][i];
                    }
                    if(f[i-1][j-w[1][i]]){
                        f[i][j]+=f[i-1][j-w[1][i]];
    //                    from[i][j]=j-w[1][i];
                    }
                }
            }
            if(f[cnt][p1]!=1){
                printf("no
    ");continue;
            }
            //统计答案 
            int ans[maxn],tot=0,mj=p1;
            for(int i=cnt;i>=1;i--)
            if(f[i-1][mj-w[0][i]]==1){
                for(int j=1;j<=p1+p2;j++)
                if(find(j)==p[i] &&d[j]==0)ans[++tot]=j;
                mj-=w[0][i];
            }
            else{
                for(int j=1;j<=p1+p2;j++)
                if(find(j)==p[i] && d[j]==1)ans[++tot]=j;
                mj-=w[1][i];
            }
            sort(ans+1,ans+1+tot);
            for(int i=1;i<=tot;i++)printf("%d
    ",ans[i]);
            printf("end
    ");
        }
    }
  • 相关阅读:
    vscode中golang插件配置
    好用的go开源模块汇总
    常用linux指令
    读大嘴生平咨询---->关于商业模式的思考
    photoprism 源码阅读
    C语言的算法
    C语言输出函数和输入函数
    C语言基本概念
    servlet(3)
    servlet(2)
  • 原文地址:https://www.cnblogs.com/superminivan/p/11545474.html
Copyright © 2011-2022 走看看