这里的几个判断是通过下面这个博客,搞懂了轮廓线的定义以及状态转移的:

https://www.cnblogs.com/iiyiyi/p/5846864.html


不过,这个图的轮廓线应该是这样的:

我们当前需要做出来的决策是点(i,j),之前的1代表之前的状态已经确定好(否则是不合法的)

红色线标注出来的是轮廓线,蓝色线标注的是二进制的ID号,是从上到下,从左到右,从大到小标号的

所以,我们当前(i,j)格子有两种方案:

A:不摆~

B:摆,想摆一定是有条件的,同时满足四个条件

条件1:左上角没有摆:左上角存在,且没有摆

条件2:上面没有摆:上面存在,且没有摆

条件3:右上角没有摆:右上角存在,且没有摆

条件4:左面没有摆:左面存在,且没有摆

存在:用(i,j)坐标来判断

是否摆:看当前状态k的对应的二进制位是否为1~~~

(所以,看懂很简单,那为啥不压缩了就挂了。。。)


AC代码:

#include <cstdio>
#include <cstring>
using namespace std;

const int maxn = 10;
int n,K;
long long dp[2][1<<maxn][100],ans;

int getnext(int k){
	if (k & (1<<n))
		k -= 1<<n;
	return k<<1;
}

/*
i:0->n-1
j:0->n-1
*/
bool check(int i,int j,int k){
	if (i&&j&&(k&(1<<n))) return false;//左上角
	// 在第一行或者在第一列或者左上角没有国王
	if (i&&(k&(1<<(n-1)))) return false;//上
	// 在第一行或者上面没有国王
	if (i&&(j<n-1)&&(k&(1<<(n-2)))) return false;//右上角
	//在最后一列或者在第一行或者右上角没有国王
	if (j&&(k&1)) return false;//左边
	// 在第一列或者左边没有国王
	return true;
}

int main(){
	//freopen("input.txt","r",stdin);
	scanf("%d%d",&n,&K);
	int cur=0;
	dp[0][0][0]=1;
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++){
			cur ^= 1;
			memset(dp[cur],0,sizeof(dp[cur]));
			for(int k=0;k<(1<<(n+1));k++)
				for(int t=0;t<=K;t++){
					//int now=getnext(k);
					int now=(k<<1)%(1<<(n+1));
					dp[cur][now][t]+=dp[cur^1][k][t];
					if (t<K&&check(i,j,k)) dp[cur][now+1][t+1]+=dp[cur^1][k][t];
				}
		}
	ans=0;
	for(int k=0;k<(1<<(n+1));k++)
		ans+=dp[cur][k][K];
	printf("%lld\n",ans);
	return 0;
}

改成底下这样就不知道为啥会wa了,很尴尬

#include <cstdio>
#include <cstring>
using namespace std;

const int maxn = 10;
int n,K;
long long dp[maxn][maxn][1<<maxn][100],ans;

int getnext(int k){
	if (k & (1<<n))
		k -= 1<<n;
	return k<<1;
}
/*
i:0->n-1
j:0->n-1
bool check(int i,int j,int k){
	if (i&&j&&(k&(1<<n))) return false;//左上角
	// 在第一行或者在第一列或者左上角没有国王
	if (i&&(k&(1<<(n-1)))) return false;//上
	// 在第一行或者上面没有国王
	if (i&&(j<n-1)&&(k&(1<<(n-2)))) return false;//右上角
	//在最后一列或者在第一行或者右上角没有国王
	if (j&&(k&1)) return false;//左边
	// 在第一列或者左边没有国王
	return true;
}*/

//i:1->n
//j:1->n
bool check(int i,int j,int s){
    if (i>1&&j>1&&(s&(1<<n))) return 0;
    if (i>1&&(s&(1<<n-1))) return 0;
    if (i>1&&j<n&&(s&(1<<n-2))) return 0;
    if (j>1&&(s&1)) return 0;
    return 1;
}

int main(){
	scanf("%d%d",&n,&K);
	memset(dp,0,sizeof(dp));
	dp[0][n][0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<(1<<n);j++)
			for(int k=0;k<=K;k++)
				dp[i][0][j<<1][k]=dp[i-1][n][j][k];
		for(int j=1;j<=n;j++)
			for(int k=0;k<(1<<(n+1));k++)
				for(int t=0;t<=K;t++){
					int now=getnext(k);
					//int now=(k<<1)%(1<<(n+1));
					dp[i][j][now][t]+=dp[i][j-1][k][t];
					if (t<K&&check(i,j,k)) dp[i][j][now+1][t+1]+=dp[i][j-1][k][t];
				}
	}
	ans=0;
	for(int k=0;k<(1<<(n+1));k++)
		ans+=dp[n][n][k][K];
	printf("%lld\n",ans);
	return 0;
}