2018杭电多校第三场1003(状态压缩DP)

#include<bits/stdc++.h>
using namespace std;
const int mod =1e9+7;
int dp[1<<10];
int cnt[1<<10];
int ans[1<<10];
char c[10];
int t;
void add(int &x,int y)
{
    x=x+y>=mod?x+y-mod:x+y;
}
void del(int &x,int y)
{
    x=x-y<0?x-y+mod:x-y;
}
int main()
{
    int times;
    scanf("%d",&times);
    while(times--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        t=1<<n;
        int u,v;
        for(int i=0;i<t;i++)
        {
            dp[i]=0;
            cnt[i] = __builtin_popcount(i);//已经加入的边,函数返回i中位为1的个数
        }
        dp[0]=1;
        for(int k=1;k<=m;k++)
        {
            memset(ans,0,sizeof(ans));
            scanf("%s%d%d",c,&u,&v);
            u--;
            v--;
            int s=(1<<u)|(1<<v);//表示u和v两条边已被选
            if(c[0]=='+')
            {
                for(int i=t-1;i>=0;i--)//从后往前遍历表示在没有u和v两条边的集合里添加这两条边
                {
                    if(!(i&s))
                        add(dp[i^s],dp[i]);
                }
            }
            else
            {
                for(int i=0;i<t;i++)//从前往后便利表示在含有u和v两条边的集合里删除后者通过增加u和v两条边而达成前者的状态
                {
                    if(!(i&s))
                        del(dp[i^s],dp[i]);
                }
            }
            for(int i=1;i<t;i++)
        {
            add(ans[cnt[i]],dp[i]);//将含有这么多条边的状态合并计数
        }
        for(int i=2;i<=n;i+=2)
            printf("%d%c",ans[i],i==n?' ':' ');
        }
    }
    return 0;
}

/*状态压缩用于将数据转化为二进制用0和1表示状态,通常数据在十几组左右,就也就是可以用2的十几次方来代替状态,通常涉及与或异或等操作,同时大胆猜测推断dp公式,看到数据要对其敏感,朝着正确的方向去考虑。*/

保持热爱 不懈努力 不试试看怎么知道会失败呢(划掉) 世上无难事 只要肯放弃(划掉)
原文地址:https://www.cnblogs.com/ldudxy/p/9411360.html