[线性dp]花店橱窗 AcWing313

小 q 和他的老婆小 z 最近开了一家花店,他们准备把店里最好看的花都摆在橱窗里。

但是他们有很多花瓶,每个花瓶都具有各自的特点,因此,当各个花瓶中放入不同的花束时,会产生不同的美学效果。

为了使橱窗里的花摆放的最合适,他们得想个办法安排每种花的摆放位置。

可是因为小 q 和小 z 每天都太忙,没有时间设计橱窗里花的摆法,所以他们想让你帮他们求出花摆放的最大美观程度和每种花所放的位置。

每种花都有一个标识,假设杜鹃花的标识数为 1,秋海棠的标识数为 2,康乃馨的标识数为 3,所有的花束在放入花瓶时必须保持其标识数的顺序,即:

杜鹃花必须放在秋海棠左边的花瓶中,秋海棠必须放在康乃馨左边的花瓶中。

如果花瓶的数目大于花束的数目。则多余的花瓶必须空置,且每个花瓶中只能放一束花。

每种花放在不同的瓶子里会产生不同的美观程度,美观程度可能是正数也可能是负数。

上述例子中,花瓶与花束的不同搭配所具有的美观程度,如下表所示:

                         花    瓶
                  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 中则显得十分难看。

为取得最大美观程度,你必须在保持花束顺序的前提下,使花束的摆放取得最大的美学值,并求出每种花应该摆放的花瓶的编号。

输入格式

第 1 行:两个整数 F 和 V,表示共有 F 种花,V 个花瓶。

第 2 行到第 F+1 行:每行有 V 个数,表示花摆放在不同花瓶里的美观程度值 value。(美观程度和小于2^31,美观程度有正有负)

输出格式

输出有两行:第一行为输出最大美观程度和的值,第二行有 F 个数表示每朵花应该摆放的花瓶的编号。

若有多种方案,输出字典序较小的方案(美观程度不变的情况下,花尽量往前放)。

数据范围

1≤F≤V≤100,

输入样例:

3 5 
7 23 -5 -24 16
5 21 -4 10 23
-21 5 -4 -20 20

输出样例:

53
2 4 5

题意: 给出一个n行m列的矩阵,现在每行选取1个数,如果当前行选取了第k个数,那么下一行就不能选前k个数,求最终选数加和的最大值,并输出字典序最小的方案。

分析: 设状态为dp[i][j]表示考虑前i行、前j列能选到的数的最大加和。在更新dp数组时dp[i][j]可以划分为两种情况,一种是选第i行第j列的数,一种是不选。对于选的情况应该从上一行上一列的状态转移,也就是dp[i][j] = dp[i-1][j-1]+a[i][j],而不选的情况就是dp[i][j-1],dp数组初始化的时候可以把第一行手动初始化,这样会比较稳一点。至于方案可以另开一个id数组,id[i][j]表示在前i行前j列选到最大值时第i行选择第几列,跟dp数组同步更新就行,最后输出方案时从后往前输出,id[n][m]就是最后一行的列,而id[n-1][id[n][m]-1]就是倒数第二行的列,以此类推。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#define int long long
using namespace std;

int n, m, a[105][105], dp[105][105], id[105][105]; 
const int inf = 1ll<<60;

signed main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++){
			dp[i][j] = -inf;
			cin >> a[i][j];
		}
	//手动更新第一行的dp值和id值 
	dp[1][0] = -inf;
	for(int i = 1; i <= m; i++){
		dp[1][i] = dp[1][i-1];
		id[1][i] = id[1][i-1];
		if(dp[1][i] < a[1][i]){
			dp[1][i] = a[1][i];
			id[1][i] = i;
		}
	}
	for(int i = 2; i <= n; i++)
		for(int j = i; j <= m; j++){
			//如果不选a[i][j]
			dp[i][j] = dp[i][j-1]; 
			id[i][j] = id[i][j-1];
			//如果选a[i][j] 
			if(dp[i][j] < dp[i-1][j-1]+a[i][j]){
				dp[i][j] = dp[i-1][j-1]+a[i][j];
				id[i][j] = j;
			}
			//相等的情况一定取更小的,所以不用更新pre				
		}
	cout << dp[n][m] << endl;
	int st[105], top = 0, r = m;//r为右边界 
	for(int i = n; i >= 1; i--){
		st[++top] = id[i][r];
		r = id[i][r]-1;
	}
	for(int i = top; i >= 1; i--)
		cout << st[i] << " ";
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值