题意就是说有k个棋子,放在一个棋盘上,不能有相邻的棋子,问一共有多少种方法,啊咧咧,,状态压缩dp嘛,讨论了一上午,下午学弟写了一下代码,,那个惨啊,错的乱七八糟,然后我们三个人就在改啊,,改啊,其实思路还是很清晰的,状态转移方程为 :
dp[i][j][x] += dp[i - 1][j - tmp][y]; 就是说 第i行 用了 j 个棋子 对应x状态下 的方法数 就等于 它 加上 第i-1行 用了j-tmp个棋子,对应y状态 下的方法数。。
当然还需要优化,比如说一开始就要存下那些状态是合法的,用数组保存下来,还有判断x和y 之间能否成为上下行直接使用 & 。ok 代码如下
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
long long n,m,k,dp[82][22][1<<9],ans,mm;
long long mark[1<<9],len;
using namespace std;
long long num(long long x)
{
long long sum=0;
while(x)
{
if(x&1==1)
sum++;
x=x>>1;
}
return sum;
}
bool judge(long long x)
{
if(x&(x<<1)) return false;
return true;
}
int main()
{
while(scanf("%lld %lld %lld",&n,&m,&k)!=EOF)
{
ans=0;
memset(dp,0,sizeof(dp));
memset(mark,0,sizeof(mark));
len = 0;
long long tmpp = n > m ? n : m;
m = n > m ? m : n;
n = tmpp;
for(long long i=0;i<(1<<m);i++)
if(judge(i))
{
dp[1][num(i)][len]=1;
mark[len ++] = i;
}
for(long long i=2;i<=n;i++)
for(long long j=0;j<=k;j++)
for(long long x = 0 ; x < len ; x ++)
{
for(long long y = 0 ; y < len ; y ++)
{
long long tmp = num (mark[x]);
if(((mark[x] & mark[y]) == 0 ) && j >= tmp)
dp[i][j][x] += dp[i - 1][j - tmp][y];
}
}
for(long long i=0;i< len;i++)
ans += dp[n][k][i];
printf("%lld\n",ans);
}
return 0;
}
开始交各种tle,,发现m要是很大的话会爆,,然后就机智的吧行和列交换一下,妥妥的。 最坑爹的是要用long long 啊,,哎,还是欠考虑啊。
渣渣睡觉去了