hdu4283 You Are the One 区间dp 记忆化搜索or递推

You Are the One

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 3032    Accepted Submission(s): 1352

Problem Description

  The TV shows such as You Are the One has been very popular. In order to meet the need of boys who are still single, TJUT hold the show itself. The show is hold in the Small hall, so it attract a lot of boys and girls. Now there are n boys enrolling in. At
the beginning, the n boys stand in a row and go to the stage one by one. However, the director suddenly knows that very boy has a value of diaosi D, if the boy is k-th one go to the stage, the unhappiness of him will be (k-1)*D, because he has to wait for
(k-1) people. Luckily, there is a dark room in the Small hall, so the director can put the boy into the dark room temporarily and let the boys behind his go to stage before him. For the dark room is very narrow, the boy who first get into dark room has to
leave last. The director wants to change the order of boys by the dark room, so the summary of unhappiness will be least. Can you help him?

Input

  The first line contains a single integer T, the number of test cases.  For each case, the first line is n (0 < n <= 100)

  The next n line are n integer D1-Dn means the value of diaosi of boys (0 <= Di <= 100)

Output

  For each test case, output the least summary of unhappiness .

Sample Input

2
  
5
1
2
3
4
5

5
5
4
3
2
2

看了半个上午网上的题解才基本上明白了......边界处理的好微妙...dp大法好

题意说的是1 - n个屌丝男,然后通过一个小黑屋也就是个栈可以改变他们的顺序,每个人有个屌丝值,然后如果这个人排第k个,则他的不高兴值为(k - 1) * D

问所有人的不高兴值之和最小是多少

先分析这个问题,对于区间1 - n,第一个人可能是第一个上场,也可能是第n个上场,其实对于第一个人,他有可能是1 - n中第k个上场(1 <= k <= n)

为什么他可以是第1 - n中任意一个上场呢,因为

比如说这样

原始队伍:1 2 3 4 5

栈底-->2 3 4 5<--栈顶        舞台 :1

1直接上舞台,剩下的四个可以直接上,也可以先进小黑屋再上,这时候1就是第一个上了

栈底-->1 2 3 4 5<--栈顶     舞台:null

栈底-->1 2 3<--栈顶            舞台:4 5

比如这两种情况,1就是最后上场的

还有在1 - n之间的

队伍:3 4 5

栈底-->1 2<--栈顶       舞台:null

然后这时先让1 2出栈上舞台

队伍:3 4 5

栈底--><--栈顶            舞台:2 1

然后3 4 5再上

栈底--><--栈顶            舞台:2 1 3 4 5

这时候1就是排第二了,其他位置也可以相似地得到

发现了子问题

因为1可以是第1 - n中任意一个上舞台,不妨设他是第k个上的,然后发现在考虑这个问题的时候考虑哪种情况都是可以分成三部分

标号为1的屌丝本身,上舞台之后排在在1之前的k - 1个,在1之后的n - k个

然后再看在1之前上舞台的k - 1个屌丝,他们可以以同样的分析方法进行更小范围的独立分析,在1之后的n - k个也可以

1在第k个上舞台时,他的不高兴值为a[i] * (k - 1),那么只要知道他之前的所有屌丝的不高兴值之和的最小值sumpre,和他之后的所有屌丝的不高兴值之和的最小值之和sumnext,最后1 - n上的最小值就是

a[i] * (k - 1) + sumpre + sumnext

sumpre和sumnext又可以以同样的方式递归分析下去

直到精确到不可再分的区间长度为一的个人

定义函数int solve(i, j)函数返回区间[i, j]上的题目要求的最小值,那么

solve(i, j) = min(solve(i + 1, i + k - 1) + solve(i + k, j) + a[i] * (k - 1) + (sum[j] - sum[i + k - 1]) * k)      (1 <= k <= j - i + 1)

区间[i, j]

i                i  + 1, i + 2, i + 3, ..........,i + k - 1                 i + k ..........i + k + 1.........j

第k个上                             之前的k - 1个                                     之后的 j - i - k + 1个

因为在第i之后的那个区间[i + k, j]在分析的时候是内部独立考虑的,因为在考虑这个子问题的时候肯定不知道它外面的父问题是什么

所以在[i + k, j]上的最优解只是独立考虑[i + k, j]的最小值,在综合到[i, j]这个区间时由于[i, i + k - 1]这k个都在他们前面所以

[i + k, j]这个最优解还要再加上D(i + k)  *  k     + D(i + k + 1)  *  k      +     ......     +    D(j) * k,   D(x)为第x个屌丝的屌丝值

如果用前缀和来表示的话就是前j个的屌丝值和sum[j],前i + k - 1个的屌丝值和sum[i + k - 1]

D(i + k)  *  k     + D(i + k + 1)  *  k      +     ......     +    D(j) * k = (sum[j] - sum[i + k - 1]) * k

边界

发现在k = 1时

solve(i + 1, i) + solve(i + 1, j) + a[i] * (1 - 1) + (sum[j] - sum[i]) * 1

这个时候也就是第i个是排在[i, j]这个区间的第一个,可见他之前没有别人,从i + 1到j全在他后面

而且有个没有意义的solve(i + 1, i)明显要让这个值有意义只有让它等于0

solve(i + 1, j) + (sum[j] - sum[i])

在k = j - i + 1时

solve(i + 1, j) + solve(j + 1, j) + a[i] * (j - i) + (sum[j] - sum[j]) * (j - i + 1);

这个时候i排在[i, j]这个区间最后一个,从i + 1到j全在他前面

这时候又出现了solve(j + 1, j)没有意义,也是明显他要等于0,而且因为都在i前面,所以[i + 1, j]上的所有屌丝都不用补(sum[j] - sum[i + k - 1]) * k) 了,当然为0

solve(i + 1, j) + a[i] * (j - i)

实现

记忆化搜索,由于系统调用栈,相对慢一些

//记忆化搜索
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 100 + 10, INF = 0x3f3f3f3f;
int n, a[maxn], sum[maxn], dp[maxn][maxn];

int solve(int i, int j) {
	if (i > j) return 0;
	int ans = dp[i][j];
	if (ans != -1) return ans;
	ans = INF;
	for (int k = 1; k <= j - i + 1; k++) {
		ans = min(ans, solve(i + 1, i + k - 1) + solve(i + k, j) + a[i] * (k - 1) + (sum[j] - sum[i + k - 1]) * k);
	}
	return dp[i][j] = ans;
}

int main()
{
	int T;
	scanf("%d", &T);
	for (int t = 1; t <= T; t++) {
		scanf("%d", &n);
		sum[0] = 0;
		for (int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
			sum[i] = sum[i - 1] + a[i];
		}
		memset(dp, -1, sizeof(dp));
		printf("Case #%d: %d\n", t, solve(1, n));
	}
	return 0;
}

开数组直接递推,很快

形象来看像是这么个过程,就是父问题去覆盖子问题的过程,每次的覆盖范围都在扩大,最终扩大到n,将所有的子问题的

解都覆盖了,也就是所有子问题向上构成了最终问题的解,dp的奇妙之处啊啊啊啊啊啊啊啊啊。。。。

//递推
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 100 + 10, INF = 0x3f3f3f3f;
int n, a[maxn], sum[maxn], dp[maxn][maxn];

int main()
{
	int T;
	scanf("%d", &T);
	for (int t = 1; t <= T; t++) {
		scanf("%d", &n);
		sum[0] = 0;
		for (int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
			sum[i] = sum[i - 1] + a[i];
		}
		for (int i = 0; i <= n; i++) {
			for (int j = 0; j <= n; j++) {
				if (i > j)
					dp[i][j] = 0;
				else
					dp[i][j] = INF;
			}
		}
		/*l为计算的区间长度
		要先有子问题的最优解才能向上推出更大区间的最优解
		所以先是区间长度为1的最优解,然后为2。。。一直到整个区间[1, n]
		*/
		for (int l = 1; l <= n; l++) {
			for (int i = 1; i <= n; i++) {
				int j = i + l - 1;
				if (j > n) continue; //防越界
				for (int k = 1; k <= j - i + 1; k++) {
					dp[i][j] = min(dp[i][j], dp[i + 1][i + k - 1] + dp[i + k][j] + (k - 1) * a[i] + (sum[j] - sum[i + k - 1]) * k);
				}
			}
		}
		printf("Case #%d: %d\n", t, dp[1][n]);
	}
	return 0;
}
时间: 2024-11-03 05:28:20

hdu4283 You Are the One 区间dp 记忆化搜索or递推的相关文章

HDU5115 Dire Wolf 区间DP 记忆化搜索

题意:举个例子,就跟DOTA里的狼BB一样,自身有攻击力,还有光环可以提升同伴的攻击力,狼站成一排,光环只能提供给相邻的狼,打掉一直狼需要打一下,同时它也会打一下,这样你的扣血量其实就等于该狼的攻击力 方程很好想,dp[i][j]代表 打掉区间[i,j]内的狼所需最少血量,这里是闭区间,后来看到是200*200 ,那么就懒得去想方程转移了,直接记忆化搜索就可以了,注意点是 一个狼被宰了,它相邻两边的两只狼攻击力会减少,所以搜索过程 分区间搜索时边界要设定好,一开始没弄好 结果 案例一直没跑出来,

HDU 4960 Another OCD Patient(区间dp记忆化搜索)

题目大意:给你一串数字让你判断经过若干次合并,使得这个数字串变成回文串的最小成本是多少.第一行是数字串,第二行是合并连续i个数字的成本是多少. 解题思路:区间dp,可以进行记忆化搜索,如果左边比右边和大那么右边一定是小了,右边比左边大那么左边一定小了.因为保证有解.具体不太好说,直接看代码吧. Another OCD Patient Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Othe

UVA 10003 Cutting Sticks 区间DP+记忆化搜索

UVA 10003 Cutting Sticks+区间DP 纵有疾风起 题目大意 有一个长为L的木棍,木棍中间有n个切点.每次切割的费用为当前木棍的长度.求切割木棍的最小费用 输入输出 第一行是木棍的长度L,第二行是切割点的个数n,接下来的n行是切割点在木棍上的坐标. 输出切割木棍的最小费用 前话-区间dp简单入门 区间dp的入门下面博客写的非常好,我就是看的他们博客学会的,入门简单,以后的应用就得靠自己了. https://blog.csdn.net/qq_41661809/article/d

uva 10891 区间dp+记忆化搜索

https://vjudge.net/problem/UVA-10891 给定一个序列x,A和B依次取数,规则是每次只能从头或者尾部取走若干个数,A和B采取的策略使得自己取出的数尽量和最大,A是先手,求最后A-B的得分. 令 f(i,j)表示对于[i,j]对应的序列,先手可以从中获得的最大得分,那么答案可以写为  f(i,j)-(sum(i,j)-f(i,j)),也就是 2*f(i,j)-sum(i,j) 下面讨论f(i,j)的写法,显然递归的形式更好表达一些,为了防止重复的计算使用记忆化搜索.

由DAG到背包问题——记忆化搜索和递推两种解法

一.问题描述 物品无限的背包问题:有n种物品,每种均有无穷多个.第 i 种物品的体积为Vi,重量为Wi.选一些物品装到一个容量为 C 的背包中,求使得背包内物品总体积不超过C的前提下重量的最大值.1≤n≤100, 1≤Vi≤C≤10000, 1≤Wi≤1000000. 二.解题思路 我们可以先求体积恰好为 i 时的最大重量(设为d[i]),然后取d[i]中的最大值(i ≤ C).与之前硬币问题,"面值恰好为S"就类似了.只不过加了新属性--重量,相当于把原来的无权图改成带权图,即把&q

HDU ACM 4597 Play Game -&gt;区间DP+记忆化搜索

分析:两个人都足够聪明,因此每个阶段都拿最大的.dp[sa][ea][sb][eb]分别表示区间1的开始为sa,结束为ea,区间2的开始为sb,结束为eb时能拿到的最大值.之后分别从四个方向上拿,是个搜索的过程. [cpp] view plaincopyprint? #include<iostream> using namespace std; int dp[25][25][25][25];  //dp[sa][ea][sb][eb],分别表示区间1的开始,结束,区间2的开始,结束 int a

Ural 1183 Brackets Sequence(区间DP+记忆化搜索)

题目地址:Ural 1183 最终把这题给A了.. .拖拉了好长时间,.. 自己想还是想不出来,正好紫书上有这题. d[i][j]为输入序列从下标i到下标j最少须要加多少括号才干成为合法序列.0<=i<=j<len (len为输入序列的长度). c[i][j]为输入序列从下标i到下标j的断开位置.假设没有断开则为-1. 当i==j时.d[i][j]为1 当s[i]=='(' && s[j]==')' 或者 s[i]=='[' && s[j]==']'时,d

poj 1088 滑雪(区间dp+记忆化搜索)

题目链接:http://poj.org/problem?id=1088 思路分析: 1>状态定义:状态dp[i][j]表示在位置map[i][j]可以滑雪的最长区域长度: 2>状态转移方程:由于由位置[i, j]只能向四个方向移动,所以子问题最多有四个:所以dp[i][j]为其邻域可以滑雪的最大区域长度加上从该位置滑到邻域的长度,即1: 代码如下: #include <cstdio> #include <iostream> #include <algorithm&

hdu 4597 Play Game(区间dp,记忆化搜索)

Problem Description Alice and Bob are playing a game. There are two piles of cards. There are N cards in each pile, and each card has a score. They take turns to pick up the top or bottom card from either pile, and the score of the card will be added