BZOJ1087: [SCOI2005]互不侵犯King(状压DP)

题意:传送门

题解:直接暴搜爆T这是肯定的

附上暴搜代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=10+5;
int n,k,ans[maxn][maxn],res;
bool safe(int x,int y)
{
    for(int i=-1;i<=1;i++){
        for(int j=-1;j<=1;j++){
            if(i==0&&j==0)continue;
            int nx=x+i,ny=y+j;
            if(nx>=1&&nx<=n&&ny>=1&&ny<=n){
                if(ans[nx][ny]){
                    return false;
                }
            }
        }
    }
    return true;
}
void dfs(int x,int y,int cnt)
{
    if(cnt==0){
        res++;
        return;
    }
    int s=n*n-x*n+y-1;
    if(cnt>s){
        return ;
    }
    if(x==n&&y==n+1)return ;
    if(y==n+1){
        x=x+1;y=1;
    }
    if(safe(x,y)){
        ans[x][y]=1;
        dfs(x,y+1,cnt-1);
        ans[x][y]=0;
    }
    dfs(x,y+1,cnt);
}
int main()
{
    scanf("%d%d",&n,&k);
    dfs(1,1,k);
    printf("%d\n",res);
    return 0;
}


然后考虑用状压DP,用f[i][m][j]表示到了第i行以及i行以上一共用了m个国王,那么转移方程就是

f[i+1][p+cnt[j]][j]+=f[i][p][l] (cnt[l]<=p<=k-cnt[j])其中cnt数组,以及三个特判关系都可以打表处理下。

附上代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,k,num,all,cnt[512];
bool c1[512],c2[512][512];
ll f[10][100][512];
void pre()
{
    for(int i=0;i<=all;i++){
        if((i&(i>>1))==0){
            num=0;
            for(int j=i;j;j>>=1)num+=(j&1);
            c1[i]=true;cnt[i]=num;
        }
    }
    for(int i=0;i<=all;i++){
        if(c1[i]){
            for(int j=0;j<=all;j++){
                if(c1[j]){
                    if((i&j)==0&&(i&(j>>1))==0&&(i&(j<<1))==0)c2[i][j]=true;
                }
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&k);
    all=(1<<n)-1;
    pre();
    for(int i=0;i<=all;i++){
        if(c1[i]){
            f[1][cnt[i]][i]=1;
        }
    }
    for(int i=1;i<n;i++){
        for(int j=0;j<=all;j++){
            if(c1[j]){
                for(int l=0;l<=all;l++){
                    if(c1[l]&&c2[j][l]){
                        for(int p=cnt[j];p+cnt[l]<=k;p++)f[i+1][p+cnt[l]][l]+=f[i][p][j];
                    }
                }
            }
        }
    }
    ll ans=0;
    for(int i=0;i<=all;i++)ans+=f[n][k][i];
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zhouzi2018/article/details/87929399