线性DP——花店橱窗
谨以此题解献给线性dp最后一道题
题目大致
Description
xq和他的老婆xz最近开了一家花店,他们准备把店里最好看的花都摆在橱窗里。但是他们有很多花瓶,每个花瓶都具有各自的特点,因此,当各个花瓶中放入不同的花束时,会产生不同的美学效果。为了使橱窗里的花摆放的最合适,他们得想个办法安排每种花的摆放位置。
可是因为xq和xz每天都太忙,没有时间设计橱窗里花的摆法,所以他们想让你帮他们求出花摆放的最大美观程度和每种花所放的位置。
注:标号小花必须放在标号大的前面。
每种花放在不同的瓶子里会产生不同的美观程度,美观程度可能是正数也可能是负数。
上述例子中,花瓶与花束的不同搭配所具有的美观程度,如下表所示:
花 瓶
1 2 3 4 5
1 (杜鹃花) 7 23 -5 -24 16
2 (秋海棠) 5 21 -4 10 23
3 (康乃馨) -21 5 -4 -20 20
根据上表,杜鹃花放在花瓶2中,会显得非常好看;但若放在花瓶4中则显得十分难看。
Input Format
第1行:两个整数F和V,表示xq和xz一共有F种花,V个花瓶。(1<=F<=V<=100)
第2行到第F+1行:每行有V个数,表示花摆放在不同花瓶里的美观程度值value。(美观程度和不超过maxint,美观程度有正有负。)
Output Format
输出有两行:第一行为输出最大美观程度和的值,第二行有F个数表示每朵花应该摆放的花瓶的编号。
Sample
样例输入
3 5
7 23 -5 -24 16
5 21 -4 10 23
-21 5 -4 -20 20
样例输出
53
2 4 5
Hint
其实就是简单的DP,花店橱窗问题啦。
注意尽量靠前放啊!
解析
在题目中有几个比较重要的地方:
- 所有花都要按照规定的顺序排放
- 如有美观程度相等的方案,花遵循靠前放置的原则
- 本题涉及负数
我们再来看如何求解
先看样例:因为要顺序排放,所以我们先看1号花。1号花可以摆在1、2、3号花瓶,但不能摆在3,4号花瓶,否则剩下的两朵花就没有地方了。同样,2号花可以摆在2、3、4号花瓶,但不能摆在1、5上,否则1、3号花就没有位置了。
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
1 | true | true | true | false | false |
2 | false | true | true | true | false |
3 | false | false | true | true | true |
于是我们可以得出规律:我们只需要更新第\(i\)到第\(V-F+i\) (\(i\)为花的编号)的dp就可以了。也就是只更新绿色位置的dp状态。
然后,根据题目可知,花要顺序摆放,那么第\(i\)朵花也一定和第\(i-1\)的花有关。看样例:
下面的表格表示dp状态。我们令dp[花的编号][花瓶编号]
。第一朵花没有前面的花,所以直接根据美观程度更新。第4、5号位不更新。
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
1 | 7 | 23 | -5 | 0 | 0 |
2 |
接着更新dp[2],2、5号位不更新。第2号位置只能由dp[1][1]
来更新,所以直接继承。dp[2][2] = dp[1][1] + w[2][2]
。w表示不同花在不同花瓶的美观程度。
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
1 | 7 | 23 | -5 | 0 | 0 |
2 | 7+21=28 |
更新dp[2][3]
时,为了达到最大,需要找到dp[1][1]
到dp[1][2]
的最大值,也就是dp[1][2]
。
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
1 | 7 | 23 | -5 | 0 | 0 |
2 | 28 | 23+(-4)=19 |
同理可得:
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
1 | 7 | 23 | -5 | 0 | 0 |
2 | 28 | 19 | 23+10=33 |
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
1 | 7 | 23 | -5 | 0 | 0 |
2 | 7(无用) | 28 | 19 | 33 | 0 |
3 | 7(无用) | 28(无用) | 24 | 8 | 53 |
易得,最大美观值为53。所以dp状态转移方程为\(dp[i][j]\, =\, max\left \{ {dp[i-1][k]} \right \} \, +\, w[i][j]\, \, (k\in [i-1,\, j-1])\)。
由于\(i\)只和\(i-1\)有关,可以使用滚动数组优化。去掉dp一个维度的同时,注意循环中\(k\)的方向要从后往前。最后在dp[\(F\)]中找到最大答案。
记录路径也很好实现。开个数组p[i][j]
记录前驱。具体实现看代码。
#include<bits/stdc++.h>
using namespace std;
#define s(n) scanf("%d", &n)
int ed, num, dt=1, mx, v, f, ans, w[102][102], dp[102], p[102][102];
inline void pt(int a, int b){ //输出路径
if(a == 1){
printf("%d", b);
return;
}
pt(a-1, p[a][b]);
printf(" %d", b);
}
int main(){
s(f), s(v);
for(int i=1; i<=f; ++i) //读入数据
for(int j=1; j<=v; ++j)
s(w[i][j]);
for(int i=1; i<=f; ++i){
for(int j=v-f+i; j>=i; --j){ //因为要自我滚动,所以从后往前循环
for(int z=i-1; z<j; ++z){
if(z == i-1) mx = dp[z], num = z;
else if(mx < dp[z]) mx = dp[z], num = z;
}
dp[j] = mx + w[i][j];
p[i][j] = num;
if(i == f){
if(dt) ans = dp[j], dt = 0, ed = j;
else if(ans <= dp[j]) ans = dp[j], ed = j; //注意 一定是小于等于 因为要靠前放
}
}
}
printf("%d\n", ans);
pt(f, ed); //输出路径
return 0;
}
the end