scoi 互不侵犯 king

来源:互联网 发布:nyu数据科学录取名单 编辑:程序博客网 时间:2024/05/22 13:38

1087: [SCOI2005]互不侵犯King

Time Limit: 10 Sec  Memory Limit: 162 MB

Description

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。

Input

只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

Output

方案数。

Sample Input

3 2

Sample Output

16










完成情况(大视野测评)



这应该是我做的第二道状压DP了,听说是入门题,但我高了很久,最终还是看受哥的博客才懂的。。。。。。。。


用 f [ i ] [ j ] [ S ] 表示前 i 行 , 用了 j 个国王, 当前行状态为 S 的方案数

那么 f [ i ] [ j ] [ S ] = ∑ f [ i - 1 ] [ m ] [ st ];


至于状态,由于只有2^9种,并不多,所以可以dfs预处理出所有的状态


其他的不多说了,记得用long long


具体看代码,注释没删,怕自己以后看不懂了。。。。。。。


C++ AC Code

    #include<cstdio>      #include<iostream>      using namespace std;      const int N=10;      typedef long long LL;            int n,k;      LL f[N][N*N][1<<N];//f[i][j][S]表示前i行用了j个国王,第i行状态为S      int a[1<<N],m=0,num[1<<N];            void pri(int s,int deep)//_Debug_正序打印01序列      {          if(deep==n) {printf("%d",s);return;}          pri(s>>1,deep+1);          printf("%d",s&1);      }            void dfs(int x,int s)      {          if(x==n)          {              //pri(s,0);puts("");              a[++m]=s;              for(int S=s;S;S=S>>1) num[s]+=(S&1);              //printf("%d\n",num[s]);              return;          }          dfs(x+1,s<<1);          if((s&1)==0) dfs(x+1,(s<<1)+1);      }            bool can(int s,int st)      {          int S=s;          S=(S|(s<<1))|(s>>1);          bool flag=true;          for(int i=1;i<=n;i++)          {              int ss=S&1,tt=st&1;              S>>=1;st>>=1;              if(ss&tt) {flag=false;break;}          }          if(flag) return true;          else return false;      }            int main()      {          freopen("king.in","r",stdin);          freopen("king.out","w",stdout);          scanf("%d%d",&n,&k);                    dfs(0,0);//生成所有可能01方案          for(int idx=1,S=a[idx];idx<=m;idx++,S=a[idx]) f[1][num[S]][S]++;          //for(int idx=1,S=a[idx];idx<=m;idx++,S=a[idx]) printf("%d ",f[1][1][S]);          for(int i=2;i<=n;i++)          for(int j=0;j<=k;j++)          for(int idx=1,S=a[idx];idx<=m;idx++,S=a[idx])          for(int ix=1,st=a[ix];ix<=m;ix++,st=a[ix])          {              //printf("%d %d ",i,j);pri(S,0);putchar(' ');pri(st,0);puts("");              if(can(S,st)&&j-num[S]-num[st]>=0)              {                  f[i][j][S]+=f[i-1][j-num[S]][st];                  //printf("%d\n",f[i][j][S]);              }          }          //for(int idx=1,S=a[idx];idx<=m;idx++,S=a[idx]) printf("%d ",f[n][k][S]);          LL res=0;          for(int idx=1,S=a[idx];idx<=m;idx++,S=a[idx]) res+=f[n][k][S];          cout<<res<<endl;          return 0;      }  

第二天再写,发现can()函数写麻烦了,换了一种写法

    bool can(int s,int st)      {          int S=s;          S=S|(s<<1)|(s>>1);          S=S&((1<<n)-1);          if((S&st)==0) return true;          else return false;      }  

时间就快了60多ms

两次成绩:


0 0
原创粉丝点击