zoukankan      html  css  js  c++  java
  • POJ-1417 (带权并查集+计数dp)

    题目:传送门

    思路:带权并查集,分出若干个集合,每个集合缩成两个权值,如果唯一存在 每个集合中的一个权值相加后等于天使的个数,那么就能够判断出哪些是天使。这里我们可以用一个DP(i,j)表示前i个集合能组成 j 的方案数,最后记录一下路径,把每个集合的一些点加入答案中。 需要注意的是这里的初值是只有DP(0,0)才有意义,因为是恰好组成 j 的方案数,因此不能用滚动数组优化。

    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cctype>
    #include<functional>
    #include<cmath>
    #include<algorithm>
    #pragma GCC optimize(2)
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    typedef pair<double,double> pdd;
    const int N=1e3+5;
    const int inf=0x3f3f3f3f;
    const int mod=998244353;
    const double eps=1e-9;
    const long double pi=acos(-1.0L);
    #define ls (i<<1)
    #define rs (i<<1|1)
    #define fi first
    #define se second
    #define pb push_back
    #define mk make_pair
    #define mem(a,b) memset(a,b,sizeof(a))
    LL read()
    {
        LL x=0,t=1;
        char ch;
        while(!isdigit(ch=getchar())) if(ch=='-') t=-1;
        while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
        return x*t;
    }
    int n,p1,p2,tot;
    int w0[N],w1[N],f[N],v[N],dp[N][N],p[N];
    int getf(int x)
    {
        if(x!=f[x])
        {
            int t=f[x];
            f[x]=getf(f[x]);
            v[x]+=v[t];
            v[x]%=2;
        }
        return f[x];
    }
    inline int init()
    {
        tot=0;
        for(int i=1;i<=p1+p2;i++) f[i]=i,v[i]=0;
        mem(dp,0);
        mem(w0,0);
        mem(w1,0);
    }
    int main()
    {
        while(scanf("%d%d%d",&n,&p1,&p2)!=EOF&&(n||p1||p2))
        {
            init();
            for(int i=1;i<=n;i++)
            {
                int x=read(),y=read();
                char z[5];
                scanf("%s",z);
                int t=z[0]=='n';
                int fx=getf(x),fy=getf(y);
                if(fx!=fy)
                {
                    v[fy]=(-v[y]+t+v[x]+2)%2;
                    f[fy]=fx;
                }
            }
            for(int i=1;i<=p1+p2;i++)
            {
                if(i!=getf(i)) continue;
                int r=i;
                p[++tot]=r;
                for(int j=1;j<=p1+p2;j++)
                {
                    if(r!=getf(j)) continue;
                    if(v[j]%2==v[r]%2) w0[tot]++;
                    else w1[tot]++;
                }
            }
            dp[0][0]=1;
            for(int i=1;i<=tot;i++)
                for(int j=p1;j>=0;j--)
                {
                    if(j>=w0[i]) dp[i][j]+=dp[i-1][j-w0[i]];
                    if(j>=w1[i]) dp[i][j]+=dp[i-1][j-w1[i]];
                }//不能用滚动数组优化,因为只有(0,0)为1;
            if(dp[tot][p1]!=1)
            {
                printf("no
    ");
                continue;
            }
            int k=p1;
            vector<int> ans;
            for(int i=tot;i&&k;i--)
            {
                if(k>=w0[i]&&dp[i-1][k-w0[i]]==1)
                {
                    k-=w0[i];
                    int r=p[i];
                    for(int j=1;j<=p1+p2;j++)
                        if(r==getf(j)&&v[j]==0) ans.pb(j);
                }
                else if(k>=w1[i]&&dp[i-1][k-w1[i]]==1)
                {
                    k-=w1[i];
                    int r=p[i];
                    for(int j=1;j<=p1+p2;j++)
                        if(r==getf(j)&&v[j]==1) ans.pb(j);
                }
            }
            sort(ans.begin(),ans.end());
            for(int i=0;i<ans.size();i++) printf("%d
    ",ans[i]);
            printf("end
    ");
        }
        return 0;
    }
    /*
    4 4 2
    1 2 yes
    3 6 no
    4 3 no
    1 5 yes
    */
    AC
  • 相关阅读:
    SDUT-3376_数据结构实验之查找四:二分查找
    SDUT-3375_数据结构实验之查找三:树的种类统计
    SDUT-3373_数据结构实验之查找一:二叉排序树
    深度优先遍历和广度优先遍历
    SDUT-2498_AOE网上的关键路径
    SDUT-2140_判断给定图是否存在合法拓扑序列
    SDUT-2144_最小生成树
    SDUT-3364_欧拉回路
    SDUT-3363_驴友计划
    Java练习 SDUT-2271_Eddy的难题
  • 原文地址:https://www.cnblogs.com/DeepJay/p/12583645.html
Copyright © 2011-2022 走看看