一双木棋 chess

题意

link

\(n \times m, (n, m\leq 10)\) 的矩阵,每次可以在左上都有棋子的地方下,奇数次下在 \((i, j)\) 的价值是 \(a_{i, j}\),偶数次是 \(-b_{i, j}\)

奇数次时想让价值和最大,偶数次时想让价值和最小。

问最终结果。

轮廓线dp,状压dp

状态

如果第 \(i\) 行放了 \(c_i\) 个,那么它一定左到右连续的。

并且从第 \(1\) 行到第 \(n\) 行,\(c_i\) 是递减的。

状态就可以用轮廓线唯一表示。

\(0\) 表示 \(m\) 条横向的线,用 \(1\) 表示 \(n\) 条竖向的线。

\(f[S]\) 表示棋盘状态 \(S\) 到全部下完的价值。

初始和最终状态

由于转移是从最终倒回来的。

初始状态 \(f[111\cdots 0000\cdots] = 0\), 表示全部下完离全部下完的价值和是 0。

其它全部设为无穷大或无穷小。

最终状态 \(f[0000\cdots 111\cdots]\) 表示初始状态离全部下完的价值。

转移

能发现选一格,实际上的轮廓线就是把 \(1\) 和 下一位的 \(0\) 交换。

选的位置可以用轮廓线状态算出来,设为 \((x, y)\)

所以就有:
1.偶数次时

\[f[S] = \max_{S_i = 1, S_{i + 1} = 0} \{f[S \oplus (3 << i)] + a[x][y] \} \]

2.奇数次时

\[f[S] = \min_{S_i = 1, S_{i + 1} = 0} \{f[S \oplus (3 << i)] - b[x][y] \} \]

分析

状态是 \(O(2^{n + m})\) 的,转移是 \(O(n + m)\)

最终是 \(O(2^{n + m}(n + m))\)

代码

#include<bits/stdc++.h>
using namespace std;

using ll = long long;
const int MAXN = 15;
const int INF = 0x3f3f3f3f;
//const int mod = 1000000007;
int mod; 
const double eps = 1e-9; 

template <typename T>
void Read(T &x) {
	x = 0; T f = 1; char a = getchar();
	for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f;
	for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48);
	x *= f;
}

inline int add(const int &a, const int &b) { 
	static int c;
	c = a + b;
	if (c >= mod) c -= mod;
	if (c < 0) c += mod;
	return c;   
} 
inline int mul(const int &a, const int &b) {
	return 1ll * a * b % mod; 
}
int qpow(int a, int b) {
	int sum(1);
	while(b) {
		if (b & 1) sum = mul(sum, a);
		a = mul(a, a);
		b >>= 1;
	}
	return sum; 
}

int n, m;
int a[MAXN][MAXN], b[MAXN][MAXN];

int f[1 << 21]; 
bool vis[1 << 21];
int dfs(int S, int N) {
	if (vis[S]) return f[S];
	vis[S] = 1;
	f[S] = N ? - INF : INF;
	int x = n, y = 0;
	for (int i = 0; i < n + m - 1; i ++) {
		if (S >> i & 1) x --;
		else y ++; 
		if ((S >> i & 3) != 1) continue;
		int to = S ^ (3 << i);
		if (N) f[S] = max(f[S], dfs(to, N ^ 1) + a[x + 1][y + 1]);
		else f[S] = min(f[S], dfs(to, N ^ 1) - b[x + 1][y + 1]); 
	}
	return f[S];
}

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= m; j ++)
			cin >> a[i][j];
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= m; j ++)
			cin >> b[i][j];
	memset(f, 0x3f, sizeof(f)); 
	f[((1 << n) - 1) << m] = 0; 
	vis[((1 << n) - 1) << m] = 1; 
	cout << dfs((1 << n) - 1, 1);
	return 0;
} 
posted @ 2022-03-01 08:15  qjbqjb  阅读(47)  评论(0编辑  收藏  举报