1044.花瓶插花
Time Limit: 1000 MS Memory Limit: 32768 KB
Total Submission(s): 270 Accepted Submission(s): 30
Description
有m朵花和n个花瓶,要把这些花全部插入某些花瓶中,花瓶和花都是有序的,花在花瓶中的先后顺序必须与给定顺序相同,每朵花插入每个花瓶能得到的美观程度都不一定相同,选择一些和花瓶,求插花能得到的最大美观程度。
Input
第一行是两个整数m, n(1 <= m <= n <= 1000),表示花的数量和花瓶的数量。之后m行,每行n个正整数(<= 100),这m行中第i行的第j个数字表示第i朵花插入第j个花瓶的美观程度。
Output
一个整数,最大的总美观程度。
Sample Input
3 5
5 9 3 2 4
1 3 2 9 5
4 2 1 3 5
Sample Output
23
Source
Unknown
这道题一看就想到用dfs,一点一点往下搜,但是速度实在是太慢了,因为这个棋盘高达1000 * 1000,一个点一个点搜索必然超时,所以就想到动态保存一下, 也就是动态规划
状态转移方程(设矩阵为m 行 n 列):
dp[i][j] = max(dp[i-1][j-1] + val[i][j], dp[i][j-1]) (j >= i)
dp[i][j] = 0 (j < i)
为什么是这个转移方程呢,坑死我了。。。第一个容易理解
例如: 题目给的例子:
3 5
5 9 3 2 4
1 3 2 9 5
4 2 1 3 5
使用状态转移方程更新后dp数组为
5 9 9 9 9
0 8 11 18 18
0 0 9 14 23
(应该能看明白,原理就是更新可以更新的部分,将无用的部分更新成左边的最大值,便于下一行更新时利用)
至于第二个部分dp[i][j] = 0 (j < i)是真的坑人,让我wa了五六次,都绝望了。仔细想一想还是自己太菜了考虑不周全,如果j > i, a[i][j]是不能当作花瓶放花的(想一想为什么)因为在之前已经放了 i-1枝花,放的最紧凑的情况也得是i == j时才能放得下第i枝花, 所以需要保证 i >= j, 如何实现代码里会提到,很简单,但是如果不注意这一点会出大问题(例如,把题目给的样例输入的左下角数据改成100000 看看会出什么乱子)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
int a[1005][1005];
int dp[1005][1005];
int main()
{
int m, n;
scanf("%d%d",&m,&n);
for(int i = 1; i <= m; i ++)
for(int j = 1; j <= n; j ++)
scanf("%d",&a[i][j]);
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= m; i ++)
for(int j = i; j <= n; j ++){ //坑点,j不从1开始遍历,而是j = i时
dp[i][j] = max(dp[i - 1][j - 1] + a[i][j], dp[i][j - 1]);
}
printf("%d\n",dp[m][n]);
return 0;
}