HDOJ 4529 - N骑士问题 DP

     剪了我N久的枝...终于涉险飘过了...2958ms.....

     DP....因为马的范围只能影响到两行之内...所以我每两行两行处理...先把两行内放马的情况打出来....那么.两行放马10个以下互不冲突..有近5000种放法..直接dp..至少5000*5000*10=2.5*10^8..而且判断当前状态能否放在已有皇后的情况下..状态更新时,前后的马是否会互相攻击等相当耗时...

     所以...然后就是各种剪枝了.....

     开始以为这种方法就是状态压缩DP...刚才发现我这压根就没压缩状态..赤裸裸的DP了....状态压缩DP效率高很多...


Program:

#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#define ll long long
#define oo 1000000007
using namespace std; 
struct node1
{
       int h[9][2],num;
}a[5005],p;
int b[70][2505],bnum[70]; 
int n,m,dp[2][5005][11];
char arc[10][10];
void build_B()  // 当前在两行内放马的方案能和哪些放皇后的方案共存
{
       int i,j,x1,x2;
       for (i=0;i<=63;i++)  // 两行内放皇后,不考虑冲突,一共64种情况
       {  
              x1=i/8+1;  x2=i%8+1;
              for (j=1;j<=p.num;j++) 
              {
                  if (p.h[j][0]==1 && x1==p.h[j][1]) goto A;
                  if (p.h[j][0]==2 && x2==p.h[j][1]) goto A;
              }
              b[i][++bnum[i]]=m; // ok,挂到后面去....
              A: ;
       }
       return;
}
void dfs(int y,int x) // 搜索出两行内放10个以下马互不冲突的所有情况
{
       int i,j;
       if (p.num==10) return;
       for (i=1;i<=p.num;i++)
       {
            if (abs(p.h[i][0]-y)==1 && abs(p.h[i][1]-x)==2) return;
            if (abs(p.h[i][0]-y)==2 && abs(p.h[i][1]-x)==1) return;
       }
       p.num++;
       p.h[p.num][0]=y;     p.h[p.num][1]=x;
       a[++m]=p;
       build_B();
       for (j=x+1;j<=8;j++) dfs(y,j);
       for (i=y+1;i<=2;i++)
          for (j=1;j<=8;j++)
             dfs(i,j);
       p.num--;
       return;       
}
void prework()
{
       int i,j,p1,p2,k;
       memset(bnum,0,sizeof(bnum));
       m=0;  
       p.num=0;
       a[++m]=p;
       build_B();
       for (i=1;i<=2;i++)
          for (j=1;j<=8;j++)
             dfs(i,j); 
       return;
} 
bool f(int i,int j)  // 判断方案j在上,方案i在下..能否不冲突
{
       int p1,p2;
       for (p1=1;p1<=a[i].num;p1++)
          for (p2=1;p2<=a[j].num;p2++)
          {
                  if (abs(a[i].h[p1][0]-a[j].h[p2][0]-2)==2 && abs(a[i].h[p1][1]-a[j].h[p2][1])==1) return false;
                  if (abs(a[i].h[p1][0]-a[j].h[p2][0]-2)==1 && abs(a[i].h[p1][1]-a[j].h[p2][1])==2) return false;
          }         
       return true;
}
int getnow(int t)  // 将这两行皇后的情况转化成相应的代号
{
       int x1,x2;
       for (x1=1;x1<=8;x1++) 
          if (arc[t][x1]=='*') break;
       for (x2=1;x2<=8;x2++)
          if (arc[t+1][x2]=='*') break;
       x1--;  x2--;
       return x1*8+x2;       
}
int main()
{  
       int T,ii,jj,i,j,x,t,k,now,pre,ans,num1,num2; 
       prework();
       scanf("%d",&T); 
       while (T--)
       {
             scanf("%d",&n);
             for (i=0;i<=8;i++) gets(arc[i]+1);
             memset(dp,0,sizeof(dp));
             k=0;
             now=getnow(1);
             num1=bnum[now];
             for (ii=1;ii<=num1;ii++)
             {
                   i=b[now][ii];
                   dp[k][i][a[i].num]=1; 
             }
             pre=now;
             for (t=3;t<=8;t+=2)
             {
                   k=1-k; 
                   memset(dp[k],0,sizeof(dp[k]));
                   now=getnow(t);
                   num1=bnum[now];
                   num2=bnum[pre];
                   for (ii=1;ii<=num1;ii++) 
                   { 
                         i=b[now][ii];
                         for (jj=1;jj<=num2;jj++)
                         {
                               j=b[pre][jj];  
                               if (a[i].num+a[j].num>n || !f(j,i)) continue;  // 耗时!! 
                               for (x=n-a[i].num;x>=a[j].num;x--)
                                  dp[k][i][a[i].num+x]+=dp[1-k][j][x];
                         }
                   }
                   pre=now;
             }
             ans=0;
             for (i=1;i<=m;i++) ans+=dp[k][i][n];
             printf("%d\n",ans);
       }  
       return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值