StkOvflow

STACK OVERFLOW!

一言(ヒトコト)

zc大人我错了!
——wjd

ICPC2020昆明 Cities

原题链接

题目简述

给定一串数字,对于一串连续的数字,可以将它们染色成任意数字,问最少要多少次才能把这串数字全部染成同种颜色。

思路解析

我们可以对题目进行一下转化:所有数字全部染成同种颜色意味着相邻异色数对的数量为 0,那我们每次对整段的区间染色,只有以下两种情况:

1 ->

......... a b b b b a .............

当这个区间的左右端点再向外扩张 1 位的时候,如果左右的数字相同,那么将 bbbb 这一段染色全部染成 a 将会减少 2 对异色对(分别是左边的 (a,b) 和右边的 (b,a)),我们称呼这种情况下的 bbbb 这段为被夹着的区间

2 ->

......... a b b b b c .............

当这个区间的左右端点再向外扩张 1 位的时候,如果左右的数字不同,那么将 bbbb 这一段染色全部染成 a 将会减少 1 对异色对,染成 c 也同理。

接下来我们考虑一下极端的情况,也就是整个区间所有数字都不同,这种情况显然有n-1个异色对,由于没有同色的区间,所以一次只能消除 1 对,需要 n1 次操作。

然后我们可以统计被夹着的区间总数,然后用极端情况减去它就可以了

状态表示

f[l][r][l,r]

解释:这样是为了选择最佳的颜色来染色。

状态转移

对于 [l,r] 间没有能够与 l 的颜色配对的,直接跳过, f[l][r]=f[l+1][r]
对于有能与 l 的颜色配对的,找出所有的配对点(设为 m ),并分成三部分:
1. [l,m] 之间有 1 个被夹住的区间
2. [l+1,m1],继承 lm 之间的答案
3. [m,r],后半部分的答案,值得一提的是应该用 m 而非 m+1 是因为 m 点可以二次利用,同时夹住两个区间。

解题流程

  1. 去重+处理出每个数下一个相等的数的下标,这里可以采用倒序处理的方式,具体如下。
for (int i = n; i; i -- ) {
	nxt[i] = pos[a[i]];
	pos[a[i]] = i;
}

pos保存的是每个数最后出现的下标
2. 开始DP,这里采用记搜
3. 输出答案

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 5010;
int f[N][N], a[N], n, T;
int nxt[N], pos[N];

int dp(int l, int r) {
	int &v = f[l][r];
	if (~v) return v;

	if (l >= r) return v = 0;
	v = dp(l + 1, r);

	int x = nxt[l];
	while (x <= r) {
		v = max(v, dp(l + 1, x - 1) + 1 + dp(x, r));
		x = nxt[x];
	}

	return v;
}

int main() {
	scanf("%d", &T);

	while (T -- ) {
		scanf("%d", &n);
		for (int i = 1; i <= n; i ++ ) pos[i] = n + 1;

		for (int i = 1; i <= n; i ++ ) {
			scanf("%d", &a[i]);
			if (a[i] == a[i - 1]) -- i, -- n;
		}
		for (int i = n; i; i -- ) {
			nxt[i] = pos[a[i]];
			pos[a[i]] = i;
		}

		for (int i = 1; i <= n; i ++ ) 
			for (int j = i; j <= n; j ++ ) f[i][j] = 0;

		memset(f, -1, sizeof f);
		printf("%d\n", n - 1 - dp(1, n));		
	}
    
    return 0;
}
posted @   StkOvflow  阅读(34)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
点击右上角即可分享
微信分享提示