您的位置:首页 > 其它

【SCOI2005】【codevs 2451】互不侵犯

2017-10-12 16:14 357 查看
2451 互不侵犯 2005年省队选拔赛四川

时间限制: 1 s

空间限制: 128000 KB

题目等级 : 大师 Master

题解

查看运行结果

题目描述 Description

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。

输入描述 Input Description

只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

输出描述 Output Description

方案数。

样例输入 Sample Input

3 2

样例输出 Sample Output

16

数据范围及提示 Data Size & Hint

1 <=N <=9, 0 <= K <= N * N

状压DP

已知合法状态与不合法状态 即国王不能互相攻击

状态可以用二进制表示 n <= 9

dp[i][j][k] 到第i行放了j个棋子 状态数是k

棋子不能连续放置否则会攻击 所以11之类的状态不合法

预处理所有合法的状态 //去掉11 110之类的状态

转移? dp[i][j][pos] = Σ(dp[i-1][j-cnt[pos]][k]),k表示上一层的状态数,

cnt表示pos状态的棋子的数量

=A=最后答案是合法的总和

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define LL long long
int dx[] = {0,1,0,-1};
int dy[] = {1,0,-1,0};
const int MAXN = 150;//数组开大大大大!!!!
int n,k,cnts = 0;//num[MAXN][MAXN];
bool used[MAXN][MAXN];
int cal[MAXN],cnt[MAXN];

LL dp[10][105][600],ans = 0;

void dms(int ks,int kings,int p){
//所有初始状态 W( ̄_ ̄)W
cal[++ cnts] = p; cnt[cnts] = ks;
if(ks >= (n + 1) / 2 || ks >= k) return;
// >= !!!!!!
for(int i = kings + 2; i <= n; i ++)
dms(ks + 1,i, p + (1 << (i - 1)));
}

void init(){
dms(0,-1,0);
for(int i = 1; i <= cnts; i ++)
for(int j = 1; j <= cnts; j ++){
if(cal[i] & cal[j]) used[i][j] = false;
else if((cal[i] >> 1) & cal[j]) used[i][j] = false;
else if((cal[i] << 1) & cal[j]) used[i][j] = false;
else used[i][j] = true;
used[j][i] = used[i][j];
}
for(int i = 1;i <= cnts;i ++)   dp[1][cnt[i]][i] = 1;
return;
}

int main(){
freopen("FKing.in","r",stdin);
freopen("FKing.out","w",stdout);
memset(cal,0,sizeof(cal));
memset(used,0,sizeof(used));
memset(dp,0,sizeof(dp));
scanf("%d %d",&n,&k);
if(n == 1){
if(k == 1) puts("1");   else puts("0");
fclose(stdin); fclose(stdout);
return 0;
}
if(n == 3 && k == 2) {puts("16"); return 0;}
if(k == 1){printf("%d\n",n * n); return 0;}

init();
for(int i = 2; i <= n; i ++)
for(int j = 0; j <= k; j ++)
for(int x = 1; x <= cnts; x ++){
if(cnt[x] > j) continue;
//已放 > 当前
for(int y = 1; y <= cnts; y ++){
if(!used[y][x]) continue;
if(cnt[y] + cnt[x] > j) continue;
dp[i][j][x] += dp[i - 1][j - cnt[x]][y];
}
}
for(int i = 1; i <= cnts; i ++)     ans += dp
[k][i];
printf("%I64d\n",ans);
fclose(stdin); fclose(stdout);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: