2019 ICPC Malaysia National F(状态压缩)
赛后补题。看了这个题解,说是状态压缩。
以第一行的士兵为主,第二行士兵为次,即,第二行被第一行士兵匹配,更新第一行士兵的状态。
用当前第i个士兵的状态更新第i+1个士兵的状态。
f[i][j]:i为士兵的下标,j为第i个士兵的状态。(1<j<(1<<(e*2+1)))。
比如e=3,二进制 j=1000011,表示第i个士兵之前包括第i个士兵,在[i-3,i+3]范围内,第二行的士兵已被匹配了下标为i-3,i+2,i+3的士兵。而f[i][1000011]记录的则是此状态的个数
那么,第一行的i+1士兵可以 以j=1000011 可更新出f[i+1][0000111],f[i+1][001110],f[i+1][0010110],f[i+1][0100110],f[i][1000110],其中判断是否 **符合“不匹配”关系**,符合则不累加,不符合则可在状态中累加i的状态。
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> #include<vector> #define debug printf("!") using namespace std; typedef __int64 ll; const int mod=1e9+7; const int maxn=2e3+5; const int inf=0x3f3f3f3f; ll f[maxn][1<<10]={0}; int cant[maxn]={0}; inline int calleft(int x,int e) { for(int i=2*e;i>=0;i--)if((1<<i)&x)return e-i; return 0; } inline int calright(int x,int e) { for(int i=0;i<=2*e;i++)if((1<<i)&x)return e-i; return 0; } int main() { int n,e,k,i,j,u,v; scanf("%d%d%d",&n,&e,&k); for(i=1;i<=k;i++) { scanf("%d%d",&u,&v); if(u-v>e||v-u>e)continue; cant[u]|=1<<(e+u-v); } for(i=0;i<=e;i++) { if(1+e-i>n)continue; if((1<<i)&cant[1])continue; f[1][1<<i]=1; } for(i=1;i<n;i++) { for(j=1;j<1<<(e*2+1);j++) { if(i+calleft(j,e)<1)break; if(i+calright(j,e)>n)continue; v=j; if(v&(1<<(2*e)))v-=(1<<(2*e)); v<<=1; for(u=0;u<=e*2;u++) { if(i+1+e-u>n||i+1+e-u<1)continue; if((1<<u)&v||(1<<u)&cant[i+1])continue; f[i+1][v|(1<<u)]=(f[i+1][v|(1<<u)]+f[i][j])%mod; } } } ll ans=0; for(i=1;i<1<<(e*2+1);i++)ans=(ans+f[n][i])%mod; printf("%I64d\n",ans); }
2019-09-06