CF1420E

直接计算有多少对守卫被保护比较困难,可以计算有多少对守卫是没有被保护的。(只用考虑都没拿盾牌的就好了)

设有 t 个守卫拿着盾牌,初始状态为 a1n

这相当于在一个 01s 中填写 t1,将 s 中的 0 分成 t+1 个部分,设这些部分大小分别为 c1t+1,那么就是要最小化 ci×(ci1)2

定义一个 01s 的代价为由 a 通过移动一个 1 到相邻的 0 的位置的操作变成 s 的最小操作次数。

显然 1 之间的相对位置是不会发生改变的。

因此设 a1 的位置依次为 p1ts 中为 q1t,则代价为 |piqi|

有了以上的铺垫,就可以 DP 了。

f(i,j,k,l) 为考虑到第 i 位,已经填了 j1,并且最后一个 1i 的距离是 k,代价为 l 的答案。

  • 下一位不填 1f(i+1,j,k+1,l)f(i,j,k,l)+k
  • 下一位填 1f(i+1,j+1,0,l+|pj+1(i+1)|)f(i,j,k,l)

gi=(nt)(nt1)2minkf(n,t,k,i),对 gi 取个前缀 max 即可。

时空复杂度均为 O(n5),滚动数组优化一下空间是可以过的。

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 85, M = 3205;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int n, m, a[N], pos[N], cnt;
bool vis[2][M][N][N];
ll f[2][M][N][N], ans[M];

void chkmin(int p, int j, int k, int l, ll v) {
	if (!vis[p][j][k][l]) f[p][j][k][l] = v, vis[p][j][k][l] = 1;
	else f[p][j][k][l] = min(f[p][j][k][l], v);
}

int main() {
	scanf("%d", &n), m = n * (n - 1) / 2;
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
		if (a[i]) pos[++cnt] = i;
	}
	if (cnt == n || !cnt) {
		for (int i = 0; i <= m; ++i) printf("%d ", 0);
		return 0;
	}
	vis[0][0][0][0] = 1;
	for (int i = 0, p = 0; i < n; ++i, p ^= 1)
		for (int j = 0; j <= m; ++j)
			for (int k = 0; k <= i; ++k)
				for (int l = 0; l <= i; ++l) if (vis[p][j][k][l]) {
					vis[p][j][k][l] = 0;
					chkmin(p ^ 1, j, k, l + 1, f[p][j][k][l] + l);
					if (k < cnt) chkmin(p ^ 1, j + abs(pos[k + 1] - (i + 1)), k + 1, 0, f[p][j][k][l]);
				}
	for (int i = 0; i <= m; ++i) {
		ans[i] = inf;
		for (int j = 0; j <= n; ++j) if (vis[n & 1][i][cnt][j]) ans[i] = min(ans[i], f[n & 1][i][cnt][j]);
		if (i) ans[i] = min(ans[i], ans[i - 1]);
	}
	for (int i = 0; i <= m; ++i) printf("%lld ", (n - cnt) * (n - cnt - 1) / 2 - ans[i]);
	return 0;
}

上述做法常数非常大。

不过可以简化一下状态定义,设 f(i,j,k) 表示考虑了前 i 位,第 i 位上填 1,已经填了 j1,代价为 k 时候的答案。

转移就枚举下一个 1 填在哪,时间复杂度依旧为 O(n5),但是空间复杂度降为了 O(n4),该做法常数非常小。

Code:

#include <bits/stdc++.h>
using namespace std;
const int N = 80, inf = 0x3f3f3f3f;
int n, a[N+5], cnt[2], pos[N+5];
int total, mx;
int f[N+5][N*(N-1)/2+5][N+5];

int calc(int x) {
	return x * (x - 1) / 2;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
		++cnt[a[i]];
		if (a[i]) {
			pos[cnt[1]] = i;
		}
	}
	total = calc(cnt[0]);
	mx = calc(n);
	if (!cnt[1]) {
		for (int i = 0; i <= mx; ++i) printf("%d ", 0);
		return 0;
	}
	memset(f, 0x3f, sizeof f);
	for (int i = 1; i < n; ++i) {
		f[i][abs(pos[1] - i)][1] = calc(i - 1);
		for (int j = 0; j <= mx; ++j) {
			for (int k = 1; k <= i && k < cnt[1]; ++k) if (f[i][j][k] != inf) {
				int rest = cnt[1] - k;
				for (int l = i + 1; l <= n - rest + 1; ++l) {
					int nxt = j + abs(pos[k + 1] - l);
					if (nxt <= mx) f[l][nxt][k + 1] = min(f[l][nxt][k + 1], f[i][j][k] + calc(l - i - 1));
				}
			}
		}
	}
	int ans = total;
	for (int j = 0; j <= mx; ++j) {
		for (int i = cnt[1]; i <= n; ++i) if (f[i][j][cnt[1]] != inf)
			ans = min(ans, f[i][j][cnt[1]] + calc(n - i));
		printf("%d ", total - ans);
	}
	return 0;
}
posted @   Kobe303  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示