GYM102219F Military Class(状压DP)
题意:
给两个 1 - n的序列,要求序列中的数两两配对,使得配对的两个数绝对值之差小于 e ,并且还有 k 对限制,即 u 不能和 v 配对。
题解:
状压DP,
就是在N的值比较小的时候,可以用一个整数表示一行的DP状态,这个数的二进制反映了这一行的情况。
比如状态F(i, j)
假设第二维DP状态J的二进制形式是10001001,那么1就表示当前位置被访问,0就表示当前位置未访问。
因此,我们需要一些二进制操作来维护DP数组:
x>>1;//去掉末位数 x<<1;//在末尾增加一个0 x<<1|1;//在末尾增加一个1 x|1;//将末尾数置为1 x|1-1;//将末尾数置为0 x^1;//将末尾数取反 x|(1<<(i-1));//将从右往左第i个数变成1 x&~(1<<(i-1));//将从右往左第i个数变成0 x^(1<<(i-1));//将从右往左第i个数取反 x&(1<<i-1);//取后i位 x|(1<<i-1);//将后面i位变成1 x^(1<<i-1);//将后面i位取反 x|(x+1);//将末尾最后一个0变成1
观察到e的值也只有4,所以可以用状压表示配对到当前数时,已经用了9个数中的哪几个。
#include<bits/stdc++.h> using namespace std; const int maxn=2005; const int mod=1e9+7; typedef long long ll; ll f[maxn][maxn],g[maxn][maxn],ans; int n,e,k; int main () { scanf("%d%d%d",&n,&e,&k); for (int i=0;i<k;i++) { int x,y; scanf("%d%d",&x,&y); g[x][y]=1; } f[0][0]=1; for (int i=1;i<=n;i++) for (int x=0;x<(1<<2*e+1);x++) for (int j=-e;j<=e;j++) { int k=i+j; if (k<1||k>n||g[i][k]) continue; if ((x>>1)&(1<<(j+e))) continue; f[i][(x>>1)|(1<<(j+e))]=f[i][(x>>1)|(1<<(j+e))]+f[i-1][x]; f[i][(x>>1)|(1<<(j+e))]%=mod; } for (int i=0;i<(1<<2*e+1);i++) ans+=f[n][i],ans%=mod; printf("%lld\n",ans); }