HDU 6321 Problem C. Dynamic Graph Matching (状压dp)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ffgcc/article/details/81585736

题意:给定一个N个点的零图,M次操作,添加或删除一条边,每一次操作以后,打印用1,2,…N/2条边构成的匹配数。
分析:因为N的范围很小,所以可以把点的枚举状态用二进制表示集合。用一维数组dp[S]表示二进制集合为S的点集的匹配数。
每次加边操作,从大到小遍历集合,dp[S]+=dp[S-u-v];删边操作,从小到大遍历集合,dp[S]-=dp[S-u-v]。
预处理出每个1024之内每个数对应二进制含有1的个数,每次记录答案就将每个dp[S]加到ans[S]对应的二进制个数]中。

#include<bits/stdc++.h>
using namespace std;
const int maxn =1030;
const int mod = 1e9+7;
typedef long long LL;
void add(int &a,int b){a=a+b<mod?a+b:a+b-mod;}
void del(int &a,int b){a=a-b<0?a-b+mod:a-b;}
int dp[maxn],ans[15],cnt[maxn];

int main()
{
    int T,N,M,u,v;
    char op[5];
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&N,&M);
        int tot=1<<N;
        for(int i=0;i<tot;++i)
        {
            dp[i]=0;
            cnt[i] = __builtin_popcount(i);//统计二进制中1的个数
        }
        dp[0]=1;
        while(M--)
        {
            scanf("%s%d%d",op,&u,&v);
            memset(ans,0,sizeof(ans));
            u--,v--;
            int S = (1<<u)|(1<<v);                  //取只包含u,v的集合
            if(op[0]=='+')
            {
                for(int t=tot-1;~t;--t)
                    if(!(t&S)) add(dp[t^S],dp[t]);  //加上原来不包含的u,v的集合的匹配数
            }
            else
            {
                for(int t=0;t<tot;++t)
                    if(!(t&S)) del(dp[t^S],dp[t]);  //减去原来不包含u,v的集合的匹配数
            }
            for(int i=1;i<tot;++i) add(ans[cnt[i]],dp[i]);
            for(int i=2;i<=N;i+=2) printf("%d%c",ans[i],i<N?' ':'\n');
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ffgcc/article/details/81585736