2019 ICPC Malaysia National F(状态压缩)

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

猜你喜欢

转载自www.cnblogs.com/kkkek/p/11470628.html