1. 程式人生 > >bzoj 1087: [SCOI2005]互不侵犯King【狀壓dp】

bzoj 1087: [SCOI2005]互不侵犯King【狀壓dp】

main ace return cst 是否 處理 print const str

顯然是狀壓,設f[i][j][k]為1到i行選j個king,並且第i行狀態為k的方案數,判斷是否可行然後枚舉轉移即可
先把可行狀態預處理出來會變快

#include<iostream>
#include<cstdio>
using namespace std;
const int N=2005;
long long n,kk,a[N],sum[N],tot,t,m,f[10][30][1005],ans;
bool v[N][N];
int main()
{
    scanf("%lld%lld",&n,&kk);
    if(kk>(n+1)/2*(n+1)/2)
    {
        printf("0");
        return 0;
    }
    m=(1<<n)-1;
    for(int i=0;i<=m;i++)
        if(!(i&(i<<1)))
        {
            t=0;
            for(int j=i;j;j>>=1)
                t+=(j&1);
            a[++tot]=i;
            sum[tot]=t;
        }
    for(int i=1;i<=tot;i++)
        for(int j=1;j<=tot;j++)
            if(!(a[i]&a[j])&&!(a[i]&(a[j]<<1))&!((a[i]<<1)&a[j]))
                v[i][j]=1;
    for(int i=1;i<=tot;i++)
        f[1][sum[i]][a[i]]=1ll;
    for(int i=2;i<=n;i++)
        for(int j=1;j<=tot;j++)
            for(int k=1;k<=tot;k++)
                if(v[j][k])
                    for(int l=kk;l>=sum[j];l--)
                        f[i][l][a[j]]+=f[i-1][l-sum[j]][a[k]];
    for(int i=1;i<=tot;i++)
        ans+=f[n][kk][a[i]];
    printf("%lld",ans);
    return 0;
}

bzoj 1087: [SCOI2005]互不侵犯King【狀壓dp】