专栏文章

CF2051G Snakes 题解

CF2051G题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miqa22n6
此快照首次捕获于
2025/12/04 01:25
3 个月前
此快照最后确认于
2025/12/04 01:25
3 个月前
查看原文
n20n \le 20,很小,显然可以状压 dp。
注意到,操作过程中蛇的顺序保持不变,因此问题本质上是求一个 n\bm n 条蛇的排列
首先我们考虑,如果排列已经确定,如何求出最右侧蛇右端点的最小编号?
要求出这个值,我们只需要求出每相邻两条蛇之间的 最小安全距离(也就是使两条蛇不相撞的最短距离),然后全部加在一起即可。
我们记蛇 ii 右移的次数为 aia_i,左移的次数为 bib_i,那么显然蛇 i,ji,j 的最小安全距离 di,jd_{i,j} 为:整个操作过程aibj+1a_i-b_j+1 的最大值(蛇 ii 在蛇 jj 左侧)。
di,jd_{i,j} 的这个部分可以在转移前预处理,时间复杂度 O(qn)O(qn),空间复杂度 O(n2)O(n^2)
然后就可以 dp 了。
定义状态 dpS,idp_{S,i} 为:已经压入了集合 SS 中的蛇,且最后压入的是蛇 ii,此时最右侧蛇右端点的最小编号。
状态转移方程:dpS,i=minj=1n{dpS,j+dj,i}dp_{S,i}=\min\limits_{j=1}^n\lbrace dp_{S',j}+d_{j,i} \rbrace,其中 S={xxS,xi}S'=\lbrace x|x \in S,x \ne i \rbrace。初值和答案见代码。
dp 部分时间复杂度 O(2nn2)O(2^n \cdot n^2),空间复杂度 O(2nn)O(2^n \cdot n)

代码

CPP
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 30, MAXQ = 2e5 + 10, MAXS = 1 << 20;
int f[MAXN][MAXN], dp[MAXS][MAXN], cnta[MAXN], cntb[MAXN];
signed main(){
	int n, q;
	cin >> n >> q;
	for (int i = 0;i <= n - 1;i++)
		for (int j = 0;j <= n - 1;j++) f[i][j] = 1;
	while (q--){
		int ind;
		char opt;
		cin >> ind >> opt;
		ind--;
		cnta[ind] += (opt == '+');
		cntb[ind] += (opt == '-');
		for (int i = 0;i <= n - 1;i++){
			f[ind][i] = max(f[ind][i], cnta[ind] - cntb[i] + 1);
			f[i][ind] = max(f[i][ind], cnta[i] - cntb[ind] + 1);
		}
	}
	memset(dp, 0x3f, sizeof(dp));
	for (int i = 0;i <= n - 1;i++) dp[1 << i][i] = 1; //从编号为 1 的格子开始放蛇
	for (int st = 0;st <= (1 << n) - 1;st++){
		for (int i = 0;i <= n - 1;i++){
			if (!(st >> i & 1)) continue;
			for (int j = 0;j <= n - 1;j++){
				if (!(st >> j & 1)) continue;
				dp[st][i] = min(dp[st][i], dp[st - (1 << i)][j] + f[j][i]);
			}
		}
	}
	int ans = 9e18;
	for (int i = 0;i <= n - 1;i++) ans = min(ans, dp[(1 << n) - 1][i] + cnta[i]); //还需要加上最后一条蛇右移的格数
	cout << ans << endl;
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...