[SCOI2005] BZOJ 1087 互不侵犯King

来源:互联网 发布:数据库系统概论王珊 编辑:程序博客网 时间:2024/06/04 22:39

传送门

题解:

首先求出哪些行的状态是合法的,即没有相邻的棋子。

发现合法的行状态数量只有90,没有2^9=512这么多。

然后枚举相邻两行,预处理能否转移。

然后记q[i]表示第i种合法的行方案是q[i],cnt[i]表示q[i]放了多少个棋子

然后记dp[i][j][k]表示前i行第i行状态为j棋子个数是k的方案数

转移枚举哪些状态可以转移(刚才预处理过的)

然后……就没有然后了。

这提示我们状压dp有些时候可以通过预处理使得复杂度得到玄学优化

#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>#include<vector>#define N 10#define MAXT 90#define lint long longusing namespace std;int n,all,q[MAXT],top,cnt[MAXT];lint dp[N][N*N][MAXT];vector<int> g[MAXT];inline int getv(int x,int p){    return (x&(1<<(p-1)))>>(p-1);}inline int output(int x){    for(int i=1;i<=n;i++)        cout<<getv(x,i);    return 0;}inline bool check(int s){    for(int i=1;i<n;i++)        if(getv(s,i)&getv(s,i+1))            return false;    return true;}inline bool check(int s,int t){    for(int i=1;i<=n;i++)    {        int v=getv(s,i);        if(!v) continue;        if(i-1&&getv(t,i-1)) return false;        if(getv(t,i)) return false;        if(i+1<=n&&getv(t,i+1)) return false;    }    return true;}inline int calc(int s){    int cnt=0;    for(int i=1;i<=n;i++)        cnt+=getv(s,i);    return cnt;}int main(){    int k;scanf("%d%d",&n,&k);    all=(1<<n)-1;    for(int i=0;i<=all;i++)        if(check(i)) q[++top]=i;    for(int i=1;i<=top;i++)        cnt[i]=calc(q[i]);    for(int i=1;i<=top;i++)        for(int j=1;j<=top;j++)            if(check(q[i],q[j]))                g[j].push_back(i);    dp[0][0][1]=1LL;    for(int i=1;i<=n;i++)        for(int c=0;c<=k;c++)            for(int j=1;j<=top;j++)            {                if(cnt[j]>c) continue;                for(int k=g[j].size()-1;k>=0;k--)                    dp[i][c][j]+=dp[i-1][c-cnt[j]][g[j][k]];            }    lint ans=0;    for(int i=1;i<=top;i++)        ans+=dp[n][k][i];    printf("%lld\n",ans);return 0;}


原创粉丝点击