pc110801 Little Bishops(搜索)
|
![]() |
||||
![]() |
![]() |
![]() |
A bishop is a piece used in the game of chess which can only move diagonally from its current position. Two bishops attack each other if one is on the path of the other. In the figure below, the dark squares represent the reachable locations for bishop B1 from its current position. Bishops B1 and B2 are in attacking position, while B1 and B3 are not. Bishops B2 and B3are also in non-attacking position.

Given two numbers n and k, determine the number of ways one can put k bishops on an n x nchessboard so that no two of them are in attacking positions.
Input
The input file may contain multiple test cases. Each test case occupies a single line in the input file and contains two integers n(1 n
8) and k(0
k
n2).
A test case containing two zeros terminates the input.
Output
For each test case, print a line containing the total number of ways one can put the given number of bishops on a chessboard of the given size so that no two of them lie in attacking positions. You may safely assume that this number will be less than 1015.
Sample Input
8 6 4 4 0 0
Sample Output
5599888 260
分析:
方法一:暴力打表
方法二:回溯 (这个题目的要求是回溯,目前我的回溯只能超时) 方法三:dp
把图形旋转45度,就变成了只攻击水平方向和垂直方向,d[i][j]表示第 i 行放 j 个不相互攻击的象,c[i]表示第 i 行有多少个位置。 状态方程:d[i][j]=d[i-1][j]+d[i-1][j-1]*(c[i]-j+1);
心得:
比赛时实在想不出好方法,如果数据比较少打表也许是不错的选择
方法一:打表(要考虑只放0个象时也是1)

1 #include<iostream> 2 #include<cstring> 3 #define N 100 4 using namespace std; 5 int a[N][N]; 6 using namespace std; 7 int main() 8 { 9 int n,k; 10 memset(a,0,sizeof(a)); 11 a[1][1]=1; 12 a[2][1]=4; a[2][2]=4; 13 a[3][1]=9; a[3][2]=26; a[3][3]=26;a[3][4]=8; 14 a[4][1]=16;a[4][2]=92;a[4][3]=232;a[4][4]=260;a[4][5]=112;a[4][6]=16; 15 a[5][1]=25;a[5][2]=240;a[5][3]=1124;a[5][4]=2728;a[5][5]=3368;a[5][6]=1960;a[5][7]=440;a[5][8]=32; 16 a[6][1]=36;a[6][2]=520;a[6][3]=3896;a[6][4]=16428;a[6][5]=39680; 17 a[6][6]=53744;a[6][7]=38368;a[6][8]=12944;a[6][9]=1600;a[6][10]=64; 18 a[7][1]=49;a[7][2]=994;a[7][3]=10894;a[7][4]=70792;a[7][5]=282248;a[7][6]=692320; 19 a[7][7]=1022320;a[7][8]=867328;a[7][9]=389312;a[7][10]=81184;a[7][11]=5792;a[7][12]=128; 20 a[8][1]=64;a[8][2]=1736;a[8][3]=26192;a[8][4]=242856;a[8][5]=1444928;a[8][6]=5599888; 21 a[8][7]=14082528;a[8][8]=22522960;a[8][9]=22057472;a[8][10]=12448832;a[8][11]=3672448; 22 a[8][12]=489536;a[8][13]=20224;a[8][14]=256; 23 a[1][0]=a[2][0]=a[3][0]=a[4][0]=a[5][0]=a[6][0]=a[7][0]=a[8][0]=1; 24 while(cin>>n>>k&&(n||k)) 25 { 26 cout<<a[n][k]<<endl; 27 } 28 return 0; 29 }
回溯打表程序:

1 #include<iostream> 2 #include<cstring> 3 #include<fstream> 4 #define N 20 5 6 using namespace std; 7 8 ofstream fout("out.txt"); 9 10 bool diag1[N],diag2[N],flag; 11 int ans,n,k; 12 int a[N][N]; 13 14 void work(int count,int p) 15 { 16 if(p==n*n||count==k) return ; 17 int i,j; 18 i=p/n;j=p%n; 19 if(!diag1[i+j]&&!diag2[i-j+n]) 20 { 21 diag1[i+j]=diag2[i-j+n]=true; 22 if(count+1==k) ans++; 23 work(count+1,p+1); 24 diag1[i+j]=diag2[i-j+n]=false; 25 work(count,p+1); 26 } 27 else work(count,p+1); 28 } 29 30 int main() 31 { 32 memset(diag1,false,sizeof(diag1)); 33 memset(diag2,false,sizeof(diag2)); 34 int i,j; 35 for(i=1;i<=8;i++) 36 for(j=1;j<=14;j++) 37 { 38 n=i;k=j; 39 ans=0;flag=false; 40 work(0,0); 41 fout<<"a["<<i<<"]["<<j<<"]="<<ans<<";"; 42 } 43 return 0; 44 }
方法二:
回溯1(TLE)
从左到右,从上到下,依次枚举 n*n 个元素

1 #include<iostream> 2 #include<cstring> 3 #define N 20 4 5 using namespace std; 6 7 bool diag1[N],diag2[N],flag; 8 int ans,n,m; 9 10 void work(int count,int p) 11 { 12 if(p==n*n||count==m||count+n*n-p<m) return ; 13 int i,j; 14 i=p/n;j=p%n; 15 if(!diag1[i+j]&&!diag2[i-j+n]) 16 { 17 diag1[i+j]=diag2[i-j+n]=true; 18 if(count+1==m) ans++; 19 work(count+1,p+1); 20 diag1[i+j]=diag2[i-j+n]=false; 21 } 22 work(count,p+1);//这里才是回溯呀!!! 23 } 24 25 int main() 26 { 27 memset(diag1,false,sizeof(diag1)); 28 memset(diag2,false,sizeof(diag2)); 29 while(cin>>n>>m) 30 { 31 ans=0;flag=false; 32 if(m<1) ans=1; 33 else if(m<15) work(0,0); 34 cout<<ans<<endl; 35 } 36 return 0; 37 }
回溯2(TLE)
考虑的 回溯1 中枚举的太多,所以把网格顺时针旋转45度,相对于只攻击行和列,这样就转换成能放多少个车,这样就这要枚举 2*n-1 行,
实践证明是快了很多,可惜还是超了……

1 #include<iostream> 2 #include<cstring> 3 #define N 20 4 using namespace std; 5 6 bool visited[N]; 7 int n,m,ans; 8 9 void work(int k,int count) 10 { 11 int c,r,i,j; 12 if(count==m) ans++; 13 if(k==2*n-1||count==m||count+2*(n-1)-k+1<m) return ; 14 else 15 { 16 c=k%n;r=k/n; 17 if(k>=n) 18 { 19 c=n-1;r=k%n+1; 20 } 21 for(i=c,j=r;j<=c;j++,i--) 22 { 23 if(!visited[i-j+n]) 24 { 25 visited[i-j+n]=true; 26 work(k+1,count+1); 27 visited[i-j+n]=false; 28 //work(k+1,count) 我老喜欢放在这里 29 } 30 } 31 work(k+1,count);//回溯在这里呀 32 } 33 } 34 35 int main() 36 { 37 while(cin>>n>>m&&(n||m)) 38 { 39 memset(visited,false,sizeof(visited)); 40 ans=0; 41 if(m<1) ans=1; 42 else if(m<15) work(0,0); 43 cout<<ans<<endl; 44 } 45 return 0; 46 }