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\n");
            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\n",ans[i]);
        printf("end\n");
    }
    return 0;
}
/*
4 4 2
1 2 yes
3 6 no
4 3 no
1 5 yes
*/
AC

猜你喜欢

转载自www.cnblogs.com/DeepJay/p/12583645.html