poj_1390 动态规划

题目大意

将一些连续的序列根据颜色分为N段,每段有颜色 为 Ci, 长度为 Li。每次点击其中的一段 i ,则可以将该段i消除,该段相邻的两段自动连接到一起,如果连接到一起的两段之前的颜色相同,则更新该段的长度。消除过程可以得到得分 Li*Li。求当所有的段都消除完毕时的最多得分。

分析

求最优化问题,典型的动态规划。 
(1)考虑前n个段被消除最大得分,求dp[n]? 
    显然不行,因为无法保证无后效性,因为这种递推从前向后,如果先消除了前i个段,则前i个段中可能和后面的段合并后再消除的情况就不存在了,即无法表示所有的情况。这样就导致无法构成递推关系,即即使知道了dp[0], dp[1], ....dp[n-1], 也无法知道 dp[n]; 
(2)考虑 段i到段j之间被消除的最大得分,求 dp[0][n-1]? 即区间动态规划 
    既然要求完全消除 [i...j] 的得分,则必定要先消除一个,就从最后一个段j开始。此时有两种选择: 
        1、直接消除j,得分Lj*Lj. 
        2、先消除j之前的某些段,遇到一个颜色和j相同的段,将该段和j进行合并(由于j之前可能有多个间隔的和j颜色相同的段s,因此需要进行枚举取最大值) 
    但是,在第二种选择中,将j和某个段s进行合并之后,得到一个新的段,此时是直接消除该大段,还是继续选择将该大段和之前的某个段s1合并后再消除? 
    如果需要构造递推关系,则有 dp[i][j] = max(dp[i][j], dp[i][s‘] + dp[s+1][j-1]) 
其中 dp[s+1][j-1]是将 s到j之间的先消除,将s和j进行合并,设s和j合并后得到的段为s‘,dp[i][s‘]是将i到 s‘进行合并后的得分。 
    这样做,明显会改变原来的段的大小结构,多个段进行交叉操作时会乱,无法保证无后效性。

(3)进一步考虑: 
dp数组,dp[i][j][k] 表示从下i到j的连续的段,且j之后有长度为k的颜色和 Seq[j]相同,但是不和j相邻的段, 
将 [i, j] 内的段完全消除,且将j之后的那长度为k的段也消除,能够得到的最大分数 
注意,消除的时候,分值不包括 j到长度为k的段之间的那些数据消除的得分

这样构造状态,就可以形成递推关系: 
dp[i][j][k] = dp[i][j-1][0] + (k+Lj)*(k+Lj) 
dp[i][j][k] = max(dp[i][j][k], dp[i][s][k + Lj] + dp[s+1][j-1][0])

实现(c++)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#define MAX_SEG_NUM 202

//保存每段的数据,颜色和长度
struct Seg{
	int color;
	int length;
};
Seg gSegs[MAX_SEG_NUM];
//dp数组,dp[i][j][k] 表示从下i到j的连续的段,且j之后有长度为k的颜色和 Seq[j]相同,但是不和j相邻的段,
//将 [i, j] 内的段完全消除,且将j之后的那长度为k的段也消除,能够得到的最大分数
//注意,消除的时候,分值不包括 j到长度为k的段之间的那些数据消除的得分
int dp[MAX_SEG_NUM][MAX_SEG_NUM][MAX_SEG_NUM];

int Square(int x){
	return x*x;
}
//dp函数。采用递归式的动态规划
int GetScore(int start, int end, int ex_len){
	//如果已经求完,则直接返回
	if (dp[start][end][ex_len])
		return dp[start][end][ex_len];

	int result = Square(gSegs[end].length + ex_len); //将段j和j之后长度为k的段进行合并,直接消除求出结果
	if (start == end)	//递归结束
		return dp[start][end][ex_len] = result;

	result += GetScore(start, end - 1, 0);
	//方案一:将段j和j之后长度为k的段进行合并消除,然后加上 i 到j 之间的段进行求和(注意 ex_len 为0是因为
	//在求 dp[i][j][k]的时候,分值不包括 j到长度为k的段之间的那些数据的得分,因此 求 i到j-1之间的消除得分即可,即 ex_len = 0

	//方案二:在将j和j之后长度为k的段放在一块之后,先不消除,而是递归到子问题:
	//从i到j之间找出s,使得s的颜色和j的颜色相同,然后先消去 s+1到j-1 之间的部分SS,则子问题 为 dp[i][s][length(j) + k] + SS
	//从 SS中取出最大值
	for (int i = end - 1; i >= start; i--){
		if (gSegs[i].color != gSegs[end].color)
			continue;
		int tmp = GetScore(start, i, ex_len + gSegs[end].length) + GetScore(i + 1, end - 1, 0);
		if (tmp > result)
			result = tmp;
	}
	return dp[start][end][ex_len] = result;
}
int main(){
	int cas, c = 1;
	int total_num, color, last_color, k;
	scanf("%d", &cas);
	while (c <= cas){
		scanf("%d", &total_num);
		last_color = -1;
		k = -1;
		memset(dp, 0, sizeof(dp));
		for (int i = 0; i < total_num; i++){
			scanf("%d", &color);
			if (last_color != color){
				k++;
				last_color = color;
				gSegs[k].length = 0;
				gSegs[k].color = color;
			}
			gSegs[k].length++;
		}

		printf("Case %d: %d\n", c++, GetScore(0, k, 0));
	}
	return 0;
}
时间: 2024-08-26 17:35:58

poj_1390 动态规划的相关文章

Leetcode 494 Target Sum 动态规划 背包+滚动数据

这是一道水题,作为没有货的水货楼主如是说. 题意:已知一个数组nums {a1,a2,a3,.....,an}(其中0<ai <=1000(1<=k<=n, n<=20))和一个数S c1a1c2a2c3a3......cnan = S, 其中ci(1<=i<=n)可以在加号和减号之中任选. 求有多少种{c1,c2,c3,...,cn}的排列能使上述等式成立. 例如: 输入:nums is [1, 1, 1, 1, 1], S is 3. 输出 : 5符合要求5种

活动选择的贪心算法与动态规划(未完成)

// greedy_algorithm.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> #include<queue> using namespace std; #define NofActivity 11 int c[NofActivity + 1][NofActivity + 1]; int reme[NofActivity + 1][NofActivity + 1]; //活动的

求不相邻金币相加和的最大值--动态规划1

求不相邻金币相加和的最大值. 输入n个金币的金币面值(正数自定义),求这些金币不相邻和的最大值. 动态规划问题1 设f(n)为第n个金币数的最大值,f(0)=0,f(1)=a[1],输入的数组从下标为1开始. f(n)=max{a[n]+f(n-2),f(n-1)}. 代码如下: import java.util.Scanner; public class Jin_bi_zui_da_zhi { public static void main(String[] args) { Scanner s

[动态规划] 黑客的攻击 Hacker&#39;s CrackDown Uva 11825

抽象为数学模型就是,  取尽可能多的互不相交的子集 ,  使得每一个子集都能覆盖全集 #include <algorithm> #include <cstring> #include <cstdio> using namespace std; int n; int P[1000],cover[1000],f[1000]; int main(){ scanf("%d", &n); for (int i = 0; i < n;i++) {

Beauty Of algorithms(七)动态规划 钢条分割 矩阵链乘 最长公共子序列 最优二叉树

1.动态规划                动态规划的方法与方法类似,英文"dynamic programming",这里的programming不是程序的意思,而是一种表格法.都是通过组合子问题的解来解决原问题,分治方法将划分为互不相交的子问题,递归的求解子问题,再将它们的解组合起来求出原问题的解.与之相反动态规划应用于子问题的重叠情况,即不同的子问题具有公共的子问题,子问题的求解是递归进行 的,将其划分为更小的子问题,动态规划,每个子问题只求解一次,将其保存在表格中,从而无需每次求

Hdoj 1176 免费馅饼 【动态规划】

免费馅饼 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 26110    Accepted Submission(s): 8905 Problem Description 都说天上不会掉馅饼,但有一天gameboy正走在回家的小径上,忽然天上掉下大把大把的馅饼.说来gameboy的人品实在是太好了,这馅饼别处都不掉,就掉落在他身旁的1

Fibonacci斐波拉契数列----------动态规划DP

n==10 20 30 40 50 46 体验一下,感受一下,运行时间 #include <stdio.h>int fib(int n){ if (n<=1)     return 1; else            return fib(n-1)+fib(n-2); }int main( ){ int n; scanf("%d",&n); printf("%d\n" ,fib(n) );} 先 n==10 20 30 40 50 46

pat 1068 动态规划/Fina More Conis

1068. Find More Coins (30) Eva loves to collect coins from all over the universe, including some other planets like Mars. One day she visited a universal shopping mall which could accept all kinds of coins as payments. However, there was a special re

动态规划(4)——最长上升子序列(作业题NYOJ201)

作业题 描述 小白同学这学期有一门课程叫做<数值计算方法>,这是一门有效使用数字计算机求数学问题近似解的方法与过程,以及由相关理论构成的学科-- 今天他们的Teacher S,给他们出了一道作业题.Teacher S给了他们很多的点,让他们利用拉格朗日插值公式,计算出某严格单调函数的曲线.现在小白抄下了这些点,但是问题出现了,由于我们的小白同学上课时走了一下神,他多抄下来很多点,也就是说这些点整体连线不一定还是严格递增或递减的了.这可怎么处理呢.为此我们的小白同学制定了以下的取点规则: 1.取