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
  • 相关阅读:
    poj3669 广搜
    检索所有课程都选修的的学生的学号与姓名
    UVA10160 Servicing Stations
    uva11205 The broken pedometer 子集生成
    poj1101 the game 广搜
    poj3009 Curling 2.0 深搜
    poj 1564 Sum It Up 搜索
    HDU 2268 How To Use The Car (数学题)
    codeforces 467C George and Job(简单dp,看了题解抄一遍)
    HDU 2267 How Many People Can Survive(广搜,简单)
  • 原文地址:https://www.cnblogs.com/DeepJay/p/12583645.html
Copyright © 2011-2022 走看看