记录一个菜逼的成长。。
题目链接
PS:这是旧OJ的题,代码在新OJ里同样的题却T了,,或许加强数据了吧。然而我外加了个数组,却wa了。让我有点怀疑确定不是数据的问题?
题目:
有一个n*m的棋盘(n、m≤80,n*m≤80)要在棋盘上放k(k≤20)个棋子,使得任意两个棋子不相邻(每个棋子最多和周围4个棋子相邻)。求合法的方案总数。
经典的棋盘问题。
显然直接暴搜会T。
对于这类数据在10左右的而且搜索又会T的,普通dp又难以实现的,可以考虑状态压缩。。
dp[i][j][k] := 表示在第i行,已经放了j个棋子,此时第i行的状态为k。
可以得到状态转移方程
dp[i][j][x]+=dp[i−1][j−num(x)][y]
(num(x)表示状态为x的1的个数,也就是放了几个棋子)
我们要考虑如何去判状态的合法性。
先考虑一行的状态是否合法,就是不会有两个棋子相邻。
假设状态为x
可以用x&(x<<1)是否为0表示是否合法,为0则合法,否则不合法。
这个我们可以枚举一行的状态,预处理出合法的状态保存在数组里。
然后我们要考虑如何判相邻行的状态是否合法,这个比较容易。
假设上一行的状态为y,当前为x。
可以这样判断x&y是否为0,为0则合法,否则非法
ac代码。不过在新oj里会T。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ALL(v) (v).begin(),(v).end()
#define cl(a,b) memset(a,b,sizeof(a))
#define clr clear()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long LL;
const int maxn = 9;
LL dp[81][21][1<<maxn];
int mark[1<<maxn];
int num(int x)
{
int ret = 0;
while(x){
ret += x & 1;
x >>= 1;
}
return ret;
}
bool check(int x)
{
if(x & (x<<1))return false;
return true;
}
int main()
{
int n,m,k;
while(~scanf("%d%d%d",&n,&m,&k)){
cl(dp,0);
if(n > m)swap(n,m);
int limit = (1<<n),len = 0;
for( int i = 0; i < limit; i++ ){
if(check(i)){
dp[1][num(i)][len] = 1;
mark[len++] = i;
}
}
for( int i = 2; i <= m; i++ ){
for( int j = 0; j <= k; j++ ){
for( int x = 0; x < len; x++ ){
for( int y = 0; y < len; y++ ){
int tmp = num(mark[x]);
if(((mark[x] & mark[y]) == 0) && j >= tmp){
dp[i][j][x] += dp[i-1][j-tmp][y];
}
}
}
}
}
LL ans = 0;
for( int i = 0; i < len; i++ ){
ans += dp[m][k][i];
}
printf("%lld\n",ans);
}
return 0;
}
我加了个保存合法状态的1的个数的数组a[],在新oj的题里就wa了。不知道为什么。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ALL(v) (v).begin(),(v).end()
#define cl(a,b) memset(a,b,sizeof(a))
#define clr clear()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long LL;
const int maxn = 9;
LL dp[81][21][1<<maxn];
int mark[1<<maxn],a[1<<maxn];
int num(int x)
{
int ret = 0;
while(x){
ret += x & 1;
x >>= 1;
}
return ret;
}
bool check(int x)
{
if(x & (x<<1))return false;
return true;
}
int main()
{
int n,m,k;
while(~scanf("%d%d%d",&n,&m,&k)){
cl(dp,0);cl(a,0);
if(n > m)swap(n,m);
int limit = (1<<n),len = 0;
for( int i = 0; i < limit; i++ ){
if(check(i)){
dp[1][a[len] = num(i)][len] = 1;
mark[len++] = i;
}
}
for( int i = 2; i <= m; i++ ){
for( int j = 0; j <= k; j++ ){
for( int x = 0; x < len; x++ ){
for( int y = 0; y < len; y++ ){
int tmp = a[x];
if(((mark[x] & mark[y]) == 0) && j >= tmp){
dp[i][j][x] += dp[i-1][j-tmp][y];
}
}
}
}
}
LL ans = 0;
for( int i = 0; i < len; i++ ){
ans += dp[m][k][i];
}
printf("%lld\n",ans);
}
return 0;
}