Ykimna
2019-10-26 11:25:32
我也不知道我是否能讲清楚,如果哪里有疑问可以评论,或私信我(我在退役之前都能回答你)
这道题题意非常简单就不再赘述,直接来讲讲数据处理方法。
如果一般的动态规划去想这道题,你会发现它的状态很难去表示和转移( 当然你也可以去开个10维数组 ),这就需要引入一种方法去表示它的状态,我们可以用一个数来表示它的状态,这个数每位只有两个数0或1,对于这道题来说1为国王,0为空格。
比如:10101 表示的就是:
很显然如果棋盘有9格那么这个数会到1e8数组会炸,所以我们就需要用个方法来减维,那就是 状压 。因为这个数每位只0或1,那么很显然我们可以用二进制来表达这个状态,再把这个二进制转化为十进制来存储,1e8就就会退化为
接下来就来讲一下思路:我们先来处理每一行棋盘的所有合法状态。根据题意每个国王的左右不能有国王也就是只能插空站,在判断国王是否相邻(状态是否合法)有一种简单的方法 & 运算。这里举两个例子。
我先把这个数左移一位再与这个数进行 & 运算
我们发现合法序列的在计算后的值为
代码如下:
int cnt=0;
for(int i=0;i<=(1<<n)-1;i++)
{
if(i&(i<<1)) continue;
sta[++cnt]=i;int gw=i; //存合法状态
while(gw>0)//计算国王数量
{
if(gw%2==1) king[cnt]++;
gw>>=1;
}
}
之后的大致思路就是比较所有
if(sta[j]&sta[k]) continue;//j枚举的是Y行的合法状态,k为X行的合法状态
if((sta[j]<<1)&sta[k]) continue;
if(sta[j]&(sta[k]<<1)) continue;
判断合法的状态都讲了,就可开始
for(int c=0;c<=ki;c++)
{
if(king[j]+c>ki) break;
dp[i][j][king[j]+c]+=dp[i-1][k][c];
}
下面贴完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=(1<<10)+20;
const int M=17;
int n,ki,cnt,king[N];
long long ans,sta[N],dp[M][N][M*M];
int main()
{
cin>>n>>ki;
for(int i=0;i<=(1<<n)-1;i++)
{
if(i&(i<<1)) continue;
sta[++cnt]=i;int gw=i; //存合法状态
while(gw>0)//计算国王数量
{
if(gw%2==1) king[cnt]++;
gw>>=1;
}
}
for(int i=1;i<=cnt;i++)//初始化第一排,因为我们枚举的是X与X-1行
if(king[i]<=ki) //一行的国王数不可能超过国王的总数量
dp[1][i][king[i]]=1;
for(int i=2;i<=n;i++)//枚举每行
{
for(int j=1;j<=cnt;j++)//X行的合法状态
{
for(int k=1;k<=cnt;k++)//X-1(Y)行的合法状态
{
if(sta[j]&sta[k]) continue;
if((sta[j]<<1)&sta[k]) continue;
if(sta[j]&(sta[k]<<1)) continue;
for(int c=0;c<=ki;c++)//国王数
{
if(king[j]+c>ki) break;
dp[i][j][king[j]+c]+=dp[i-1][k][c];
}
}
}
}
for(int i=1;i<=cnt;i++)//因为记录的是前i行,所以加上在前n行的所有合法状态
ans+=dp[n][i][ki];
cout<<ans<<endl;
return 0;
}
码字不易,反正不要钱多少赞一个