洛谷 P1896 [SCOI2005]互不侵犯

P1896 [SCOI2005]互不侵犯

状压DP(思路:预处理——>判相容——>状态转移——>输出)

原理回顾

二进制表示:
1.sit[j]表示国王情况(1有0无)
2.sta[j]表示国王个数

几个关键点

0.正确的预处理

void dfs(int x, int num, int cur) {//预处理每一个状态
  if (cur >= n) {  // 处理完毕
    sit[++cnt] = x;
    sta[cnt] = num;
    return;//新建状态
  }
  dfs(x, num, cur + 1);  // cur位置不放国王
  dfs(x + (1 << cur), num + 1,cur + 2);  // cur位置放国王,相邻位置不放
}

1. 状态转移方程

j是当前行国王情况sit[j],k是i-1行国王情况sit[k];第三维表示国王个数sta;累加

f[i][j][s]+=f[i-1][k][s-sta[j]];

2. 判断相邻

位运算

sit[j]&sit[k](上下有重复的king)

(sit[j]<<1)&sit[k](左上右下有重复king)

sit[j]&(sit[k]<<1)(右上左下有重复king)

插播:位运算复习

& 按位与:同1为1
| 按位或:同0为0
^ 按位异或:同为0,异为1
~ 按位取反:0变1,1变0
num << i 左移:num*2^i(没超出范围时)
num >> i 右移:num/2^i(向下取整)
  1. n的2进制表示中第k位是几
  • n>>k (所求移到最后一位)
  • 看个位数 n&1
  1. lowbit(x):返回x的最后一位1(带上后缀0
  • x&-x
  • 原理:x&(~x+1)负数补码
  • 应用:统计1的个数,树状数组

细节+易错点

1.不开long long 见祖宗

2.小心变量

完整代码

#include<iostream>
#include<algorithm>

using namespace std;
int sit[2000], sta[2000];
long long dp[100][2000][100];
int n,k, cnt = 0;

void dfs(int x, int num, int cur) {
	if (cur >= n) {
		sit[++cnt] = x;
		sta[cnt] = num;
		return;
	}
	dfs(x, num, cur + 1);
	dfs(x + (1 << cur), num + 1, cur + 2);
}

bool hexie(int j, int k) {
	if (sit[j] & sit[k] || (sit[j] << 1) & sit[k] || sit[j] & (sit[k] << 1))
		return false;
	return true;
}

int main() {
	cin >> n>>k;
	dfs(0, 0, 0);
	for (int j = 1; j <= cnt; j++)
		dp[1][j][sta[j]] = 1;
	for (int i = 2; i <= n; i++) 
		for(int j=1;j<=cnt;j++)
			for (int x = 1; x <= cnt; x++) {
				if (!hexie(j, x))
					continue;
				for (int s = sta[j]; s <= k; s++)
					dp[i][j][s] += dp[i - 1][x][s - sta[j]];
			}
	long long ans = 0;
	for (int i = 1; i <= cnt; i++)
		ans += dp[n][i][k];
	cout << ans << endl;

	return 0;
}
posted @ 2022-04-29 23:21  Sakana~  阅读(23)  评论(1编辑  收藏  举报