c++区间dp

其实没什么好说的,多练些题就好了。

P1775 石子合并(弱化版)

石子合并(弱化版)

题目描述

设有 N ( N ≤ 300 ) N(N \le 300) N(N300) 堆石子排成一排,其编号为 1 , 2 , 3 , ⋯   , N 1,2,3,\cdots,N 1,2,3,,N。每堆石子有一定的质量 m i ( m i ≤ 1000 ) m_i(m_i \le 1000) mi(mi1000)。现在要将这 N N N 堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻。合并时由于选择的顺序不同,合并的总代价也不相同。试找出一种合理的方法,使总的代价最小,并输出最小代价。

输入格式

第一行,一个整数 N N N

第二行, N N N 个整数 m i m_i mi

输出格式

输出文件仅一个整数,也就是最小代价。

样例 #1

样例输入 #1

4
2 5 3 1

样例输出 #1

22
#include

using namespace std;
int n;
int a[305];
int s[305];
int f[305][305];
int INF = 2147483647;
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];
	for (int i = 1; i <= n; i++)  
		f[i][i] = 0;
	for (int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i];
	for (int len = 2; len <= n; len++)  
		for (int i = 1; i <= n - len + 1; i++) { 
			int j = i + len - 1;  
			f[i][j] = INF;
			for (int k = i; k <= j - 1; k++)
				f[i][j] = min (f[i][j], f[i][k] + f[k + 1][j]);
			f[i][j] += s[j] - s[i - 1]; 
 	}
	cout << f[1][n] << endl;

	return 0;
}

P1880 [NOI1995] 石子合并

[NOI1995] 石子合并

题目描述

在一个圆形操场的四周摆放 N N N 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 2 2 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出一个算法,计算出将 N N N 堆石子合并成 1 1 1 堆的最小得分和最大得分。

输入格式

数据的第 1 1 1 行是正整数 N N N,表示有 N N N 堆石子。

2 2 2 行有 N N N 个整数,第 i i i 个整数 a i a_i ai 表示第 i i i 堆石子的个数。

输出格式

输出共 2 2 2 行,第 1 1 1 行为最小得分,第 2 2 2 行为最大得分。

样例 #1

样例输入 #1

4
4 5 9 4

样例输出 #1

43
54

提示

1 ≤ N ≤ 100 1\leq N\leq 100 1N100 0 ≤ a i ≤ 20 0\leq a_i\leq 20 0ai20

#include

using namespace std;
const int N = 1e3 + 10;
int n;
int a[N];
int f[N][N], g[N][N];
int s[N];
int INF = 2147483647;
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];
	for (int i = n + 1; i <= 2 * n; i++) a[i] = a[i - n];
	for (int i = 1; i <= 2 * n; i++) s[i] = s[i - 1] + a[i];
	for (int i = 1; i <= 2 * n; i++)  f[i][i] = g[i][i] = 0;
	for (int len = 2; len <= n; len++)
		for (int i = 1; i <= 2 * n - len + 1; i++) {
			int j = i + len - 1;
			f[i][j] = INF;
			g[i][j] = 0;
			for (int k = i; k <= j - 1; k++) {
				f[i][j] = min (f[i][j], f[i][k] + f[k + 1][j]);
				g[i][j] = max (g[i][j], g[i][k] + g[k + 1][j]);
			}
			f[i][j] += s[j] - s[i - 1];
			g[i][j] += s[j] - s[i - 1];
		}
	int mi = INF, ma = 0;
	for (int i = 1; i <= n; i++) {
		mi = min(mi, f[i][i + n - 1]);
		ma = max(ma, g[i][i + n - 1]);
	}
	cout << mi << endl;
	cout << ma << endl;
	return 0;
}
//'INF' was not declared in this scope

P1063 [NOIP2006 提高组] 能量项链

[NOIP2006 提高组] 能量项链

题目描述

在 Mars 星球上,每个 Mars 人都随身佩带着一串能量项链。在项链上有 N N N 颗能量珠。能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数。并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记。因为只有这样,通过吸盘(吸盘是 Mars 人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量。如果前一颗能量珠的头标记为 m m m,尾标记为 r r r,后一颗能量珠的头标记为 r r r,尾标记为 n n n,则聚合后释放的能量为 m × r × n m \times r \times n m×r×n(Mars 单位),新产生的珠子的头标记为 m m m,尾标记为 n n n

需要时,Mars 人就用吸盘夹住相邻的两颗珠子,通过聚合得到能量,直到项链上只剩下一颗珠子为止。显然,不同的聚合顺序得到的总能量是不同的,请你设计一个聚合顺序,使一串项链释放出的总能量最大。

例如:设 N = 4 N=4 N=4 4 4 4 颗珠子的头标记与尾标记依次为 ( 2 , 3 ) ( 3 , 5 ) ( 5 , 10 ) ( 10 , 2 ) (2,3)(3,5)(5,10)(10,2) (2,3)(3,5)(5,10)(10,2)。我们用记号 ⊕ \oplus 表示两颗珠子的聚合操作, ( j ⊕ k ) (j \oplus k) (jk) 表示第 j , k j,k j,k 两颗珠子聚合后所释放的能量。则第 4 4 4 1 1 1 两颗珠子聚合后释放的能量为:

( 4 ⊕ 1 ) = 10 × 2 × 3 = 60 (4 \oplus 1)=10 \times 2 \times 3=60 (41)=10×2×3=60

这一串项链可以得到最优值的一个聚合顺序所释放的总能量为:

( ( 4 ⊕ 1 ) ⊕ 2 ) ⊕ 3 ) = 10 × 2 × 3 + 10 × 3 × 5 + 10 × 5 × 10 = 710 ((4 \oplus 1) \oplus 2) \oplus 3)=10 \times 2 \times 3+10 \times 3 \times 5+10 \times 5 \times 10=710 ((41)2)3)=10×2×3+10×3×5+10×5×10=710

输入格式

第一行是一个正整数 N N N 4 ≤ N ≤ 100 4 \le N \le 100 4N100),表示项链上珠子的个数。第二行是 N N N 个用空格隔开的正整数,所有的数均不超过 1000 1000 1000。第 i i i 个数为第 i i i 颗珠子的头标记( 1 ≤ i ≤ N 1 \le i \le N 1iN),当 i < N ii<N 时,第 i i i 颗珠子的尾标记应该等于第 i + 1 i+1 i+1 颗珠子的头标记。第 N N N 颗珠子的尾标记应该等于第 1 1 1 颗珠子的头标记。

至于珠子的顺序,你可以这样确定:将项链放到桌面上,不要出现交叉,随意指定第一颗珠子,然后按顺时针方向确定其他珠子的顺序。

输出格式

一个正整数 E E E E ≤ 2.1 × 1 0 9 E\le 2.1 \times 10^9 E2.1×109),为一个最优聚合顺序所释放的总能量。

样例 #1

样例输入 #1

4
2 3 5 10

样例输出 #1

710

提示

NOIP 2006 提高组 第一题

#include
#define int long long
#define PP pair <int, int>
using namespace std;
const int N = 1e2 + 10;
int n, a[2 * N], f[2 * N][2 * N];

signed main () {
	//freopen (".in", "r", stdin);
	//freopen (".out", "w", stdout);
	ios::sync_with_stdio (false);
	cin.tie (0), cout.tie (0);
	cin >> n;
	for (int i = 1; i <= n; i ++ ) cin >> a[i], a[i + n] = a[i];
	for (int l = 1; l <= n; l ++ ) 
		for (int i = 1; i + l <= (n << 1); i ++ ) {
			int j = i + l;
			for (int k = i + 1; k < j; k ++ ) 
				f[i][j] = max (f[i][j], f[i][k] + f[k][j] + a[i] * a[j] * a[k]);
		}
	int ans = 0;
	for (int i = 1; i <= n; i ++ )
		ans = max (ans, f[i][i + n]);
	cout << ans << endl;	
	return 0;
}

P1220 关路灯

关路灯

题目描述

某一村庄在一条路线上安装了 n n n 盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少)。老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯。

为了给村里节省电费,老张记录下了每盏路灯的位置和功率,他每次关灯时也都是尽快地去关,但是老张不知道怎样去关灯才能够最节省电。他每天都是在天亮时首先关掉自己所处位置的路灯,然后可以向左也可以向右去关灯。开始他以为先算一下左边路灯的总功率再算一下右边路灯的总功率,然后选择先关掉功率大的一边,再回过头来关掉另一边的路灯,而事实并非如此,因为在关的过程中适当地调头有可能会更省一些。

现在已知老张走的速度为 1 m / s 1m/s 1m/s,每个路灯的位置(是一个整数,即距路线起点的距离,单位: m m m)、功率( W W W),老张关灯所用的时间很短而可以忽略不计。

请你为老张编一程序来安排关灯的顺序,使从老张开始关灯时刻算起所有灯消耗电最少(灯关掉后便不再消耗电了)。

输入格式

第一行是两个数字 n n n(表示路灯的总数)和 c c c(老张所处位置的路灯号);

接下来 n n n 行,每行两个数据,表示第 1 1 1 盏到第 n n n 盏路灯的位置和功率。数据保证路灯位置单调递增。

输出格式

一个数据,即最少的功耗(单位: J J J 1 J = 1 W × s 1J=1W\times s 1J=1W×s)。

样例 #1

样例输入 #1

5 3
2 10
3 20
5 20
6 30
8 10

样例输出 #1

270

提示

样例解释

此时关灯顺序为 3 4 2 1 5

数据范围

1 ≤ n ≤ 50 1\le n\le50 1n50 1 ≤ c ≤ n 1\le c\le n 1cn

#include
#define For(i, a, b) for (int i = a; i <= b; i++)
using namespace std;
const int N = 55;
const int INF = 0x3f3f3f3f;
int n, m;
int J[N], w[N];
int s[N];
int f[N][N][2];
int main() {
	cin >> n >> m;
	For (i, 1, n) cin >> J[i] >> w[i];
	For (i, 1, n) s[i] = s[i - 1] + w[i];
	For (i, 1, n) For (j, 1, n) f[i][j][1] = f[i][j][0] = INF;
	f[m][m][1] = f[m][m][0] = 0;
	For (l, 2, n) For (i, 1, n - l + 1) {
		int j = i + l - 1;
		f[i][j][0] = min (f[i + 1][j][0] + (J[i + 1] - J[i]) * (s[i] + s[n] - s[j]), f[i + 1][j][1] + (J[j] - J[i]) * (s[i] + s[n] - s[j]));
		f[i][j][1] = min (f[i][j - 1][0] + (J[j] - J[i]) * (s[i - 1] + s[n] - s[j - 1]), f[i][j - 1][1] + (J[j] - J[j - 1]) * (s[i - 1] + s[n] - s[j - 1]));
	}
	int ans = min (f[1][n][0], f[1][n][1]);
	cout << ans << endl;
	return 0;	
}
//2147483647

P2308 添加括号

添加括号

题目背景

给定一个正整数序列a(1),a(2),…,a(n),(1<=n<=20)

不改变序列中每个元素在序列中的位置,把它们相加,并用括号记每次加法所得的和,称为中间和。

例如:

给出序列是4,1,2,3。

第一种添括号方法:

((4+1)+(2+3))=((5)+(5))=(10)

有三个中间和是5,5,10,它们之和为:5+5+10=20

第二种添括号方法

(4+((1+2)+3))=(4+((3)+3))=(4+(6))=(10)

中间和是3,6,10,它们之和为19。

题目描述

现在要添上n-1对括号,加法运算依括号顺序进行,得到n-1个中间和,求出使中间和之和最小的添括号方法。

输入格式

共两行。 第一行,为整数n。(1< =n< =20) 第二行,为a(1),a(2),…,a(n)这n个正整数,每个数字不超过100。

输出格式

输出3行。 第一行,为添加括号的方法。 第二行,为最终的中间和之和。 第三行,为n-1个中间和,按照从里到外,从左到右的顺序输出。

样例 #1

样例输入 #1

4
4 1 2 3

样例输出 #1

(4+((1+2)+3))
19
3 6 10

提示

范围在题目上有说明。

#include

using namespace std;
int n;
int a[305];
int s[305];
int f[305][305];
int INF = 2147483647;
int ans[305][305];
void dfs (int l, int r) {
	if (l == r) {
		cout << a[l];
		return;
	}
	cout << '(';
	dfs (l, ans[l][r]);
	cout << '+';
	dfs (ans[l][r] + 1, r);
	cout << ')';
}
int df (int l, int r) {
	if (l == r) return a[l];
	int res = 0;
	res += df (l, ans[l][r]);
	res += df (ans[l][r] + 1, r);
	cout << res << ' ';
	return res;
}
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];
	for (int i = 1; i <= n; i++)  
		f[i][i] = 0;
	for (int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i];
	for (int len = 2; len <= n; len++)  
		for (int i = 1; i <= n - len + 1; i++) { 
			int j = i + len - 1;  
			f[i][j] = INF;
			for (int k = i; k <= j - 1; k++)
				if (f[i][k] + f[k + 1][j] < f[i][j])	
					f[i][j] = f[i][k] + f[k + 1][j], ans[i][j] = k;
			f[i][j] += s[j] - s[i - 1]; 
 	}
 	dfs (1, n);
	cout << endl << f[1][n] << endl;
	df (1, n);
	return 0;
}

P1622 释放囚犯

释放囚犯

题目描述

Caima 王国中有一个奇怪的监狱,这个监狱一共有 P P P 个牢房,这些牢房一字排开,第 i i i 个紧挨着第 i + 1 i+1 i+1 个(最后一个除外)。现在正好牢房是满的。

上级下发了一个释放名单,要求每天释放名单上的一个人。这可把看守们吓得不轻,因为看守们知道,现在牢房中的 P P P 个人,可以相互之间传话。如果某个人离开了,那么原来和这个人能说上话的人,都会很气愤,导致他们那天会一直大吼大叫,搞得看守很头疼。如果给这些要发火的人吃上肉,他们就会安静点。

输入格式

第一行两个整数 P P P Q Q Q Q Q Q 表示释放名单上的人数;

第二行 Q Q Q 个整数,表示要释放哪些人。

输出格式

仅一行,表示最少要给多少人次送肉吃。

样例 #1

样例输入 #1

20 3
3 6 14

样例输出 #1

35

提示

样例说明 #1

先释放 14 14 14 号监狱中的罪犯,要给 1 1 1 13 13 13 号监狱和 15 15 15 20 20 20 号监狱中的 19 19 19 人送肉吃;再释放 6 6 6 号监狱中的罪犯,要给 1 1 1 5 5 5 号监狱和 7 7 7 13 13 13 号监狱中的 12 12 12 人送肉吃;最后释放 3 3 3 号监狱中的罪犯,要给 1 1 1 2 2 2 号监狱和 4 4 4 5 5 5 号监狱中的 4 4 4 人送肉吃。

数据规模与约定

  • 对于 50 % 50\% 50% 的数据, 1 ≤ P ≤ 100 1 \le P \le 100 1P100 1 ≤ Q ≤ 5 1 \le Q \le 5 1Q5
  • 对于 100 % 100\% 100% 的数据, 1 ≤ P ≤ 1 0 3 1 \le P \le 10^3 1P103 1 ≤ Q ≤ 100 1 \le Q \le 100 1Q100 Q ≤ P Q \le P QP
#include
#define LL long long
#define PP pair <int, int>
using namespace std;
const int N = 2e2 + 10;
int n, m, a[N], f[N][N];
int main () {
	//freopen (".in", "r", stdin);
	//freopen (".out", "w", stdout);
	ios::sync_with_stdio (false);
	cin.tie (0), cout.tie (0);
	cin >> m >> n;
	for (int i = 1; i <= n; i ++ ) cin >> a[i];
	sort (a + 1, a + n + 1);
	a[0] = 0, a[n + 1] = m + 1;
	for (int l = 1; l <= n; l ++ ) {
		for (int i = 1; i + l - 1 <= n; i ++ ) {
			int j = i + l - 1;
			f[i][j] = 0x3f3f3f3f;
			for (int k = i; k <= j; k ++ )
				f[i][j] = min (f[i][j], f[i][k - 1] + f[k + 1][j] + a[j + 1] - a[i - 1] - 2);
		}
	}
	cout << f[1][n] << endl;
	return 0;
}

P1043 [NOIP2003 普及组] 数字游戏

[NOIP2003 普及组] 数字游戏

题目描述

丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共 n n n 个),你要按顺序将其分为 m m m 个部分,各部分内的数字相加,相加所得的 m m m 个结果对 10 10 10 取模后再相乘,最终得到一个数 k k k。游戏的要求是使你所得的 k k k 最大或者最小。

例如,对于下面这圈数字( n = 4 n=4 n=4 m = 2 m=2 m=2):

c++区间dp_第1张图片

要求最小值时, ( ( 2 − 1 )   m o d   10 ) × ( ( 4 + 3 )   m o d   10 ) = 1 × 7 = 7 ((2-1)\bmod10)\times ((4+3)\bmod10)=1\times 7=7 ((21)mod10)×((4+3)mod10)=1×7=7,要求最大值时,为 ( ( 2 + 4 + 3 )   m o d   10 ) × ( − 1   m o d   10 ) = 9 × 9 = 81 ((2+4+3)\bmod10)\times (-1\bmod10)=9\times 9=81 ((2+4+3)mod10)×(1mod10)=9×9=81。特别值得注意的是,无论是负数还是正数,对 10 10 10 取模的结果均为非负值。

丁丁请你编写程序帮他赢得这个游戏。

输入格式

输入文件第一行有两个整数, n n n 1 ≤ n ≤ 50 1\le n\le 50 1n50) 和 m m m 1 ≤ m ≤ 9 1\le m\le 9 1m9)。以下 n n n 行每行有个整数,其绝对值 ≤ 1 0 4 \le10^4 104,按顺序给出圈中的数字,首尾相接。

输出格式

输出文件有 2 2 2 行,各包含 1 1 1 个非负整数。第 1 1 1 行是你程序得到的最小值,第 2 2 2 行是最大值。

样例 #1

样例输入 #1

4 2
4
3
-1
2

样例输出 #1

7
81

提示

【题目来源】

NOIP 2003 普及组第二题

#include
#define LL long long
#define PP pair <int, int>
using namespace std;
const int N = 55;
int maxf[2 * N][2 * N][N];
int minf[2 * N][2 * N][N];
int s[N * 2];
int n, m;
int a[2 * N];
inline int mod (int x) {
	return (x % 10 + 10) % 10;
}
int main () {
	ios::sync_with_stdio (false);
	cin.tie (0), cout.tie (0);
	cin >> n >> m;
	for (int i = 1; i <= n; i ++ ) {
		cin >> a[i];
		a[i + n] = a[i];
	}	
	for (int i = 1; i <= n + n; i ++ ) a[i] += a[i - 1];
	for (int i = 1; i <= n * 2; i ++ )
		for (int j = i; j <= n * 2; j ++ )
			maxf[i][j][1] = minf[i][j][1] = mod (a[j] - a[i - 1]);
	for (int k = 2; k <= m; k ++ )
		for (int l = 1; l <= n; l ++ ) {
			for (int i = 1; i + l <= n + n; i ++ ) {
				int j = i + l;
				minf[i][j][k] = 0x3f3f3f3f;
				for (int g = i; g < j; g ++ ) {
					maxf[i][j][k] = max (maxf[i][j][k], maxf[i][g][k - 1] * maxf[g + 1][j][1]);
					minf[i][j][k] = min (minf[i][j][k], minf[i][g][k - 1] * minf[g + 1][j][1]);
				}
			}
		}
	int res1 = 0x3f3f3f3f, res2 = -0x3f3f3f3f;
	for (int i = 1; i <= n; i ++ )
		res1 = min (res1, minf[i][i + n - 1][m]), res2 = max (res2, maxf[i][i + n - 1][m]);
	cout << res1 << endl << res2 << endl;
	return 0;
}

P1026 [NOIP2001 提高组] 统计单词个数

[NOIP2001 提高组] 统计单词个数

题目描述

给出一个长度不超过 200 200 200 的由小写英文字母组成的字母串(该字串以每行 20 20 20 个字母的方式输入,且保证每行一定为 20 20 20 个)。要求将此字母串分成
k k k 份,且每份中包含的单词个数加起来总数最大。

每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串 this 中可包含 thisis,选用 this 之后就不能包含
th

单词在给出的一个不超过 6 6 6 个单词的字典中。

要求输出最大的个数。

输入格式

每组的第一行有两个正整数 p , k p,k p,k
p p p 表示字串的行数, k k k 表示分为 k k k 个部分。

接下来的 p p p 行,每行均有 20 20 20 个字符。

再接下来有一个正整数 s s s,表示字典中单词个数。
接下来的 s s s 行,每行均有一个单词。

输出格式

1 1 1个整数,分别对应每组测试数据的相应结果。

样例 #1

样例输入 #1

1 3
thisisabookyouareaoh
4
is
a
ok
sab

样例输出 #1

7

提示

【数据范围】
对于 100 % 100\% 100% 的数据, 2 ≤ k ≤ 40 2 \le k \le 40 2k40 1 ≤ s ≤ 6 1 \le s \le 6 1s6

【样例解释】
划分方案为 this / isabookyoua / reaoh

【题目来源】

NOIP 2001 提高组第三题

#include
#define LL long long
#define PP pair <int, int>
using namespace std;
const int N = 2e2 + 10;
char a[N][N];
int g[N][N], f[N][N];
string s;
string st = " ";
int b;
int n, m;
string c[N];
int main () {
	ios::sync_with_stdio (false);
	cin.tie (0), cout.tie (0);
	cin >> n >> m;
	for (int i = 1; i <= n; i ++ ) {
		cin >> s;
		st += s;
	}
	cin >> b;
	for (int i = 1; i <= b; i ++ ) cin >> c[i];
	n = st.size () - 1;
	
	for (int i = n; i >= 1; i -- )
		for (int j = i; j >= 1; j -- ) {
			g[j][i] = g[j + 1][i];
			string res = st.substr (j, i - j + 1);
			for (int k = 1; k <= b; k ++ ) {
				string res1 = res.substr (0, c[k].size ());
				if (res1 == c[k]) {
					g[j][i] ++ ;
					break;
				}
			}			
		}
	for (int i = 1; i <= n; i ++ )
		f[i][1] = g[1][i];
	for (int i = 1; i <= m; i ++ )
		f[i][i] = f[i - 1][i - 1] + g[i][i];
	for (int i = 1; i <= n; i ++ ) 
		for (int j = 1; j <= m && j < i; j ++ )
			for (int k = j; k < i; k ++ )
				f[i][j] = max (f[i][j], f[k][j - 1] + g[k + 1][i]);
	cout << f[n][m] << endl;
	return 0;
}

P4170 [CQOI2007]涂色

[CQOI2007]涂色

题目描述

假设你有一条长度为 5 5 5 的木板,初始时没有涂过任何颜色。你希望把它的 5 5 5 个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为 5 5 5 的字符串表示这个目标: RGBGR \texttt{RGBGR} RGBGR

每次你可以把一段连续的木板涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木板涂成 RRRRR \texttt{RRRRR} RRRRR,第二次涂成 RGGGR \texttt{RGGGR} RGGGR,第三次涂成 RGBGR \texttt{RGBGR} RGBGR,达到目标。

用尽量少的涂色次数达到目标。

输入格式

输入仅一行,包含一个长度为 n n n 的字符串,即涂色目标。字符串中的每个字符都是一个大写字母,不同的字母代表不同颜色,相同的字母代表相同颜色。

输出格式

仅一行,包含一个数,即最少的涂色次数。

样例 #1

样例输入 #1

AAAAA

样例输出 #1

1

样例 #2

样例输入 #2

RGBGR

样例输出 #2

3

提示

40 % 40\% 40% 的数据满足 1 ≤ n ≤ 10 1\le n\le 10 1n10

100 % 100\% 100% 的数据满足 1 ≤ n ≤ 50 1\le n\le 50 1n50

#include
#define LL long long
#define PP pair <int, int>
using namespace std;
const int N = 55;
char a[N];
int n, f[N][N];
int main () {
	//freopen (".in", "r", stdin);
	//freopen (".out", "w", stdout);
	ios::sync_with_stdio (false);
	cin.tie (0), cout.tie (0);
	string st;
	cin >> st;
	n = st.size ();
	for (int i = 1; i <= n; i ++ ) a[i] = st[i - 1];
	for (int i = 1; i <= n; i ++ ) f[i][i] = 1;
	for (int l = 1; l <= n; l ++ ) {
		for (int i = 1; i + l <= n; i ++ ) {
			int j = i + l;
			f[i][j] = 0x3f3f3f3f;
			if (a[i] == a[j]) f[i][j] = min (f[i + 1][j], f[i][j - 1]);
			else for (int k = i; k < j; k ++ )
				f[i][j] = min (f[i][j], f[i][k] + f[k + 1][j]);
		}
	}
	cout << f[1][n] << endl;
	return 0;
}

P3146 [USACO16OPEN]248 G

[USACO16OPEN]248 G

题目描述

Bessie likes downloading games to play on her cell phone, even though she doesfind the small touch screen rather cumbersome to use with her large hooves.

She is particularly intrigued by the current game she is playing.The game starts with a sequence of N N N positive integers ( 2 ≤ N ≤ 248 2 \leq N\leq 248 2N248), each in the range 1 … 40 1 \ldots 40 140. In one move, Bessie cantake two adjacent numbers with equal values and replace them a singlenumber of value one greater (e.g., she might replace two adjacent 7swith an 8). The goal is to maximize the value of the largest numberpresent in the sequence at the end of the game. Please help Bessiescore as highly as possible!

给定一个1*n的地图,在里面玩2048,每次可以合并相邻两个(数值范围1-40),问序列中出现的最大数字的值最大是多少。注意合并后的数值并非加倍而是+1,例如2与2合并后的数值为3。

输入格式

The first line of input contains N N N, and the next N N N lines give the sequence

of N N N numbers at the start of the game.

输出格式

Please output the largest integer Bessie can generate.

样例 #1

样例输入 #1

4
1
1
1
2

样例输出 #1

3

提示

In this example shown here, Bessie first merges the second and third 1s to

obtain the sequence 1 2 2, and then she merges the 2s into a 3. Note that it is

not optimal to join the first two 1s.

#include
#define LL long long
#define PP pair <int, int>
using namespace std;
const int N = 255;
int n, f[N][N], ans;
int main () {
	//freopen (".in", "r", stdin);
	//freopen (".out", "w", stdout);
	ios::sync_with_stdio (false);
	cin.tie (0), cout.tie (0);
	cin >> n;
	for (int i = 1; i <= n; i ++ ) cin >> f[i][i];
	for (int l = 1; l <= n; l ++ ) {
		for (int i = 1; i + l <= n; i ++ ) {
			int j = i + l;
			for (int k = i; k < j; k ++ )
				if (f[i][k] == f[k + 1][j]) f[i][j] = max (f[i][j], f[i][k] + 1);
			ans = max (ans, f[i][j]);
		}
	}
	cout << ans << endl;
	return 0;
}

P1040 [NOIP2003 提高组] 加分二叉树

[NOIP2003 提高组] 加分二叉树

题目描述

设一个 n n n 个节点的二叉树 tree \text{tree} tree 的中序遍历为 ( 1 , 2 , 3 , … , n ) (1,2,3,\ldots,n) (1,2,3,,n),其中数字 1 , 2 , 3 , … , n 1,2,3,\ldots,n 1,2,3,,n 为节点编号。每个节点都有一个分数(均为正整数),记第 i i i 个节点的分数为 d i d_i di tree \text{tree} tree 及它的每个子树都有一个加分,任一棵子树 subtree \text{subtree} subtree(也包含 tree \text{tree} tree 本身)的加分计算方法如下:

subtree \text{subtree} subtree 的左子树的加分 × \times × subtree \text{subtree} subtree 的右子树的加分 + + + subtree \text{subtree} subtree 的根的分数。

若某个子树为空,规定其加分为 1 1 1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。

试求一棵符合中序遍历为 ( 1 , 2 , 3 , … , n ) (1,2,3,\ldots,n) (1,2,3,,n) 且加分最高的二叉树 tree \text{tree} tree。要求输出

  1. tree \text{tree} tree 的最高加分。

  2. tree \text{tree} tree 的前序遍历。

输入格式

1 1 1 1 1 1 个整数 n n n,为节点个数。

2 2 2 n n n 个用空格隔开的整数,为每个节点的分数

输出格式

1 1 1 1 1 1 个整数,为最高加分($ Ans \le 4,000,000,000$)。

2 2 2 n n n 个用空格隔开的整数,为该树的前序遍历。

样例 #1

样例输入 #1

5
5 7 1 2 10

样例输出 #1

145
3 1 2 4 5

提示

数据规模与约定

对于全部的测试点,保证 1 ≤ n < 30 1 \leq n< 30 1n<30,节点的分数是小于 100 100 100 的正整数,答案不超过 4 × 1 0 9 4 \times 10^9 4×109

#include
#define LL long long
#define PP pair <int, int>
//坚持马克思主义!打倒资本主义!
using namespace std;
const int N = 55;
int n, f[N][N], p[N][N];
void dfs (int l, int r) {
	if (l > r) return;
	cout << p[l][r] << ' ';
	if (l == r) return;
	dfs (l, p[l][r] - 1);
	dfs (p[l][r] + 1, r);
}
int main () {
	ios::sync_with_stdio (false);
	cin.tie (0), cout.tie (0);
	cin >> n;
	for (int i = 1; i <= n; i ++ )
		cin >> f[i][i], p[i][i] = i;
	for (int l = 1; l <= n; l ++ ) 
		for (int i = 1; i + l <= n; i ++ ) {
			int j = i + l;
			f[i][j] = f[i + 1][j] + f[i][i];
			p[i][j] = i;
			for (int k = i + 1; k < j; k ++ )
				if (f[i][j] < f[i][k - 1] * f[k + 1][j] + f[k][k])
					f[i][j] = f[i][k - 1] * f[k + 1][j] + f[k][k], p[i][j] = k;
		}
	cout << f[1][n] << endl;
	dfs (1, n);
	return 0;
}

CF607B Zuma

Zuma

题面翻译

题目描述

Genos最近在他的手机上下载了祖玛游戏。在祖玛游戏里,存在n个一行的宝石,第i个宝石的颜色是Ci 。这个游戏的目标是尽快的消灭一行中所有的宝石。 在一秒钟,Genos能很快的挑选出这些有颜色的宝石中的一个回文的,连续的子串,并将这个子串移除。每当一个子串被删除后,剩余的宝石将连接在一起,形成一个新的行列。你的任务是:求出把整个宝石串都移除的最短时间。 让我们给你一个提示:如果一个串正着读或倒着读都一样,那么这个串(或子串)叫回文串。在我们这道题中,“回文”指这个宝石串中的第一个珠子的颜色等于最后一个珠子的颜色,第二个珠子的颜色等于倒数第二个珠子的颜色,等等。
输入输出格式
输入格式:

第一行包含一个整数n(1<=n<=500) ——宝石串的长度。 第二行包含n个被空格分开的整数,第i(1<=i<=n) 个表示这行中第i个珠子的颜色。
输出格式:

输出一个整数,把这行珠子移除的最短时间。 (样例略)
说明:

在第一个例子中,Genos可以在一秒钟就把这行珠子全部移走。 在第二个例子中,Genos一次只能移走一个珠子,所以移走三个珠子花费他三秒。 在第三个例子中,为了达到2秒的最快时间,先移除回文串4 4,再移除回文串1 2 3 2 1。

感谢@Administrator2004 提供的翻译

题目描述

Genos recently installed the game Zuma on his phone. In Zuma there exists a line of $ n $ gemstones, the $ i $ -th of which has color $ c_{i} $ . The goal of the game is to destroy all the gemstones in the line as quickly as possible.

In one second, Genos is able to choose exactly one continuous substring of colored gemstones that is a palindrome and remove it from the line. After the substring is removed, the remaining gemstones shift to form a solid line again. What is the minimum number of seconds needed to destroy the entire line?

Let us remind, that the string (or substring) is called palindrome, if it reads same backwards or forward. In our case this means the color of the first gemstone is equal to the color of the last one, the color of the second gemstone is equal to the color of the next to last and so on.

输入格式

The first line of input contains a single integer $ n $ ( $ 1<=n<=500 $ ) — the number of gemstones.

The second line contains $ n $ space-separated integers, the $ i $ -th of which is $ c_{i} $ ( $ 1<=c_{i}<=n $ ) — the color of the $ i $ -th gemstone in a line.

输出格式

Print a single integer — the minimum number of seconds needed to destroy the entire line.

样例 #1

样例输入 #1

3
1 2 1

样例输出 #1

1

样例 #2

样例输入 #2

3
1 2 3

样例输出 #2

3

样例 #3

样例输入 #3

7
1 4 4 2 3 2 1

样例输出 #3

2

提示

In the first sample, Genos can destroy the entire line in one second.

In the second sample, Genos can only destroy one gemstone at a time, so destroying three gemstones takes three seconds.

In the third sample, to achieve the optimal time of two seconds, destroy palindrome 4 4 first and then destroy palindrome 1 2 3 2 1.

#include
using namespace std;
const int N = 505;
const int inf = 2147483647;
int n, a[N], f[N][N];
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++) f[i][j] = inf; 
	for (int i = 1; i <= n; i++) f[i][i] = 1;
	for (int i = 1; i < n; i++) 
		if (a[i] == a[i + 1]) f[i][i + 1] = 1;
		else f[i][i + 1] = 2;
	for (int i = 3; i <= n; i++) 
		for (int j = 1; i + j - 1 <= n; j++) {
			int l = j, r = i + j - 1;
			if (a[l] == a[r]) f[l][r] = f[l + 1][r - 1];
			for (int k = l; k < r; k++) f[l][r] = min (f[l][r], f[l][k] + f[k + 1][r]);
		}
	cout << f[1][n] << endl;
	return 0;
}

注:以上题目均来自洛谷想要提交题目,请前往原站

你可能感兴趣的:(dp动态规划,c++,算法,图论)