欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

2018HDU多校3-Problem C. Dynamic Graph Matching(hdu 6321)-状压dp

程序员文章站 2022-03-13 19:28:05
...

2018HDU多校3-Problem C. Dynamic Graph Matching(hdu 6321)-状压dp

题意:

有n个点,m个操作,操作为增加一条边或删除一条边,每次操作后输出1~n/2个边的匹配个数,k边匹配的定义为:取k条边为一组,这一组边中没有公共点,求能找出多少组

思路:

因为n的范围2~10,数据很小,考虑状压dp,设计dp方程,dp[now][S]表示now状态下,点集为S时,已经匹配的方案数。
对于加边操作,dp[i][S]=dp[i-1][S]+dp[i-1][S-(u,v)],对应两种情况,即(u,v)没有边的情况,和(u,v)有边的情况
对于减边操作,dp[i][S]=dp[i][S]-dp[i][S-(u,v)]
类似于背包问题的滚动数组,可以将dp数组变成一维,即dp[S]
加边,dp[S]+=dp[S-(u,v)]
减边,dp[S]-=dp[S-(u,v)]

代码:

#include <iostream>
using namespace std;
const int mod=1e9+7;
int cnt[100005];
int dp[100005];
int ans[100005];
void add(int &a,int b)   //加边操作
{
    a+=b;
    if(a>=mod) a-=mod;
}
void sub(int &a,int b)    //减边操作
{
    a-=b;
    if(a<0) a+=mod;
}
int main()
{
    std::ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
    {
        int n,m;
        cin>>n>>m;
        int t=1<<n;          //用二进制表示n个点的状态
        for(int i=0;i<t;i++)
        {
            cnt[i]=__builtin_popcount(i);    //计算i的二进制表示中有多少个1
            dp[i]=0;    //初始
        }
        dp[0]=1;   //点集为空时,初始结果为1
        while(m--)
        {
            char a;
            int u,v;
            cin>>a>>u>>v;
            u--,v--;                                         //位移量从0开始计算
            int S=(1<<u)|(1<<v);               //用S表示选择了u,v点的状态
            if(a=='+')
            {
                for(int i=t-1;i>=0;i--)           //遍历全部状态
                {
                    if((i&S)==0)                     //某状态中包含了u,v两点
                    {
                        add(dp[i^S],dp[i]);      //加操作,i^S即表示S-(u,v),不包含u,v两点的情况
                    }
                }
            }
            if(a=='-')
            {
                for(int i=0;i<t;i++)              //同上,进行减操作
                {
                    if((i&S)==0)
                    {
                        sub(dp[i^S],dp[i]);
                    }
                }
            }
            for(int i=0;i<=n;i++) ans[i]=0;        //初始化
            for(int i=1;i<t;i++)
            {
                add(ans[cnt[i]],dp[i]);            //将最后答案赋给ans,cnt[i]表示i的二进制中1个个数,其意义为当前点集中有边点的个数
            }
            for(int i=2;i<=n;i+=2)                      //因为两个点构成一条边,所以从2开始,每次+2
            {
                if(i==2) cout<<ans[i];           
                else
                    cout<<" "<<ans[i];
            }
            cout<<endl;
        }
    }
    return 0;
}
相关标签: ACM