Codeforcs 1114D Flood Fill (区间DP or 最长公共子序列)

题意:给你n个颜色块,颜色相同并且相邻的颜色块是互相连通的(连通块)。你可以改变其中的某个颜色块的颜色,不过每次改变会把它所在的连通块的颜色也改变,问最少需要多少次操作,使得n个颜色块的颜色相同。

例如:[1, 2, 2, 3, 2]需要2步:[1, 2, 2, 3, 2] -> [1, 2, 2, 2, 2] -> [2, 2, 2, 2, 2]。

思路:我们先把颜色块压缩(即把本来颜色相同并且相邻的颜色块合并)。容易发现,如果出现了形如[1, 2, 3, 1]这种情况, 那么显然先合并两个1中间的部分,再把中间部分变成1操作次数会更少,于是我们就可以得到2种思路:

法1:区间DP:设dp[l][r]是合并[l, r]之间的颜色块的最小的代价,那么分两种情况:

1: l - 1和r + 1颜色相同,dp[l - 1][r + 1] = min(dp[l - 1][r + 1], dp[l][r] + 1);//l - 1和r + 1颜色相同,一步就可以转移到

2: l - 1和r + 1颜色不同,dp[l - 1][r] = min(dp[l - 1][r], dp[l][r] + 1);dp[l][r + 1] = min(dp[l][r + 1], dp[l][r] + 1);//l - 1和r + 1颜色不同,那只能一步转移到l - 1, r或者l, r + 1

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5010;
int dp[maxn][maxn];
int a[maxn], b[maxn], tot;
int main() {
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
	}
	for (int i = 1; i <= n; i++) {
		if(a[i] == a[i + 1]) continue;
		b[++tot] = a[i];
	}
	memset(dp, 0x3f, sizeof(dp));
	for (int i = 1; i <= tot; i++) dp[i][i] = 0;
	for (int len = 1; len <= tot; len++) {
		for (int l = 1; l <= tot - len + 1; l++) {
			int r = l + len - 1;
			if(b[l - 1] == b[r + 1]) {
				dp[l - 1][r + 1] = min(dp[l - 1][r + 1], dp[l][r] + 1);
			}
			else {
				dp[l - 1][r] = min(dp[l - 1][r], dp[l][r] + 1);
				dp[l][r + 1] = min(dp[l][r + 1], dp[l][r] + 1);
			}
		}
	}
	printf("%d\n", dp[1][tot]);
}

法2:假设n个颜色块的颜色都各不相同,那么合并需要n - 1次。如果出现了[1, 2, 3, 1]这种情况,那么合并次数可以少一次。如果是[4, 1, 2, 3, 4, 3, 2, 1]这种情况,那么先合并3到3之间,再合并2到2之间,再合并1到1之间比先合并4到4之间更优。如果我们把压缩序列上相同并且相距最近的两个数看成区间,每出现一个区间答案会减一,所以答案是n - 1 - 不相交的最多的区间的数目(区间在端点相交不算相交,包含关系不算相交)。比如[4, 1, 2, 3, 4, 3, 2, 1]这种情况,有4个区间。4:[1, 5] , 1: [2, 8], 2: [3, 7], 3: [4, 6]。显然,保留 [2, 8],  [3, 7],  [4, 6]三个区间最优,答案是8 - 1 - 3 = 4。那么怎么求不相交的最多的区间的数目呢?这个区间数目是压缩序列和反向压缩序列的的LCS / 2。例如[1, 2, 3, 1]和[1, 3, 2, 1]的LCS是3,区间个数是1。为什么呢?求lCS的过程中,我们每匹配成功一次,相当于区间的左端点找到的一个可以匹配的区间的右端点,或者是一个右端点找到一个对应的左端点,而匹配过程中是一个从前往后, 一个从后往前,所以匹配成功的区间肯定不相交。这个过程之后,每个区间被算了2次,并且会匹配出一个左端点等于右端点的区间,所以要除以2。

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5010;
int dp[maxn][maxn];
int a[maxn], b[maxn], c[maxn], tot;
int main () {
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
	}
	for (int i = 1; i <= n; i++) {
		if(a[i] == a[i + 1]) continue;
		b[++tot] = a[i];
		c[tot] = a[i];
	}
	reverse(c + 1, c + 1 + tot);
	for (int i = 1; i <= tot; i++)
		for (int j = 1; j <= tot; j++) {
			if(b[i] == c[j])
				dp[i][j] = dp[i - 1][j - 1] + 1;
			else
				dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
		}
	printf("%d\n", tot - 1 - dp[tot][tot] / 2);
}

  

原文地址:https://www.cnblogs.com/pkgunboat/p/10361375.html

时间: 2024-11-07 21:17:18

Codeforcs 1114D Flood Fill (区间DP or 最长公共子序列)的相关文章

UVA 10100- Longest Match(dp之最长公共子序列)

题目地址:UVA 10100 题意:求两组字符串中最大的按顺序出现的相同单词数目. 思路:将字串中的连续的字母认作一个单词,依次计算出两个字符串中的单词,其中第1个字符串的单词序列为t1.word[1]-..t1.word[n],第2个字符串的单词序列为t2.word[1]-..t2.word[m].然后将每个单词当成一个字符,使用LCS算法计算出两个字符串的最长公共子序列,该序列的长度就是最长匹配. #include <stdio.h> #include <math.h> #in

dp之最长公共子序列

例1:给你两个字符串,找出最长子序列的长度. 对于字符串t, 字符串s,给定特定的i, j代表t,s的位置,只存在三种情况: 1. i == 0 ||  j==0, M[i][j] = 0; 2. t[i] == s[j], M[i][j] = min(M[j-1][j-1] + 1, M[i-1][j], M[i][j-1]);//.....不知道怎么解释.... 3. t[i] != s[j] , M[i][j] = min(M[i-1][j], M[i][j-1]);//..... 经典d

【DP】最长公共子序列

Description 字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列.令给定的字符序列X="x0,x1,-,xm-1",序列Y="y0,y1,-,yk-1"是X的子序列,存在X的一个严格递增下标序列<i0,i1,-,ik-1>,使得对所有的j=0,1,-,k-1,有xij = yj.例如,X="ABCBDAB",Y="BCDB"是X的一个子序列. 对给

(hdu step 3.2.2)Common Subsequence(简单dp:求最长公共子序列的长度)

在写题解之前给自己打一下广告哈~..抱歉了,希望大家多多支持我在CSDN的视频课程,地址如下: http://edu.csdn.net/course/detail/209 题目: Common Subsequence Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 976 Accepted Submission(s): 538   Probl

[dp]LCS最长公共子序列

https://www.51nod.com/tutorial/course.html#!courseId=4 复杂度:${\rm O}(nm)$ 转移方程: 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int n,m; 5 int dp[1002][1002]; 6 char path[1002]; 7 string s,t; 8 int main(){ 9 cin>>s>

hdu1159-Common Subsequence(DP:最长公共子序列LCS)

Common Subsequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 49216    Accepted Submission(s): 22664 Problem Description A subsequence of a given sequence is the given sequence with some el

简单Dp----最长公共子序列,DAG最长路,简单区间DP等

/* uva 111 * 题意: * 顺序有变化的最长公共子序列: * 模板: */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int a[100]; int mu[100]; int Dp[100][100]; int main() { int n,x; scanf("%d", &n

POJ 1159 Palindrome(区间DP/最长公共子序列+滚动数组)

Palindrome Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 56150   Accepted: 19398 Description A palindrome is a symmetrical string, that is, a string read identically from left to right as well as from right to left. You are to write a

hdu 4352 数位dp(最长上升子序列的长度为k的个数)

http://acm.hdu.edu.cn/showproblem.php?pid=4352 Problem Description #define xhxj (Xin Hang senior sister(学姐)) If you do not know xhxj, then carefully reading the entire description is very important. As the strongest fighting force in UESTC, xhxj grew