Link:http://acm.hdu.edu.cn/showproblem.php?pid=6321
这道题是激发我学状压的题,很感激,即使现在还一知半解,DP还是比较玄幻的
题意:
一开始给你n个独立节点,m次操作,操作分两类, 1 加边 2 删除边。 问你每次操作后,选择k = 1、2、3、4……n/2 条边,这k条边没有公共节点。
我对这道题的理解:
状态state表示当前集合的节点存在情况,本题从边转化为点,边无公共节点,那么在加入一条边(u,v)时,状态的转移就是每一个一个未包含u且未包含v的状态 k ,转移到 k | (1 < < u) | (1 < < v) 。最后我们在计算边为1、2、3……n/2 时,等级于计算 节点数为 2、4、6……n 的匹配数。
代码:
#include <bits/stdc++.h>
#define maxn 2005
using namespace std;
const int mod=1e9+7;
int cnt[maxn+5];
int dp[maxn],ans[25];
int bit(int s)
{
int tmp = 0;
while(s)
{
if(s&1)
tmp++;
s=s>>1;
}
return tmp;
}
void init()
{
//cout<<bit(14)<<endl;
for(int i=0;i<maxn;i++)
{
cnt[i] = bit(i);
}
}
int main()
{
int t;
scanf("%d",&t);
init();
while(t--){
int n,m;
scanf("%d%d",&n,&m);
int high = (1<<n);
memset(dp,0,sizeof(dp));
dp[0] = 1;
while(m--)
{
char opt[2];
int u,v;
scanf("%s%d%d",opt,&u,&v);
u--;
v--;
int tmp = (1<<u) | (1<<v);
if(opt[0] == '+')
{
for(int s = 0; s < high ; s++)
{
if(!(s&(tmp)))
{
dp[s|tmp] = (dp[s|tmp] + dp[s])%mod;
}
}
}
else
{
for(int s = 0; s < high ; s++)
{
if(!(s&(tmp)))
{
dp[s|tmp] = (dp[s|tmp]-dp[s] + mod)%mod;
}
}
}
memset(ans,0,sizeof(ans));
for(int i=1;i<high;i++)
{
ans[cnt[i]] = (ans[cnt[i]] + dp[i])%mod;
}
for(int i = 2;i<=n;i+=2)
{
printf("%d%c",ans[i]," \n"[i==n]);
}
}
}
}