动态规划? 解决花店橱窗

题目描述

小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。(美观程度和小于2312^{31}231,美观程度有正有负)

输出描述:

输出有两行:第一行为输出最大美观程度和的值,第二行有F个数表示每朵花应该摆放的花瓶的编号。
若有多种方案,输出字典序较小的方案(美观程度不变的情况下,花尽量往前放)。

示例1

输入

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

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

输出

53 2 4 5

53
2 4 5

备注:

1≤F≤V≤100,1\leq F\leq V\leq 100,1≤F≤V≤100,

这道题的数据范围不大,可以考虑较为暴力的方法,下面给出一个On方的解法。

这里我们假设有m种花,n种花瓶,,题目需要你在每一行找到一个数,这个数相对上一行的列数一定是在右边,也就是j1<j2<j3<...jn-1<jn,因此每一行能取到的有效范围就是j(n-1)+1---n,最理想状况能够取到i,也就是行数等于列数,下面我们把每一行的i---n称为有效区间,只要在这个区间内取数才能保证数字串必定存在。

建立二维数组f[i][j],这里我们把f[i][j]作为到达i,j所在坐标能够取得的最大值,那么f[i][j]=a[i][j]+f[i-1][j-1](对角线数据),那么如何维护f[i][j]呢,可以在先计算完一轮f[i][j]之后再进行一次有效区间内的最大值维护,即维护后f[i][j]能够存放当前行的最大值,那么就能保证下面一行在加时都能加到能取到的最大值。

至于输出因为要取尽量靠前的,所以遇到一样的要往前找,找到这个数第一次出现的地方,一些细节就放在程序里了

#include<stdio.h>
#include<algorithm>
using namespace std;
#define ll long long
ll a[1000][1000];//原数组
ll f[1000][1000];//处理数组
int z[1000];//答案序列
int check(int i, int j)
{
	while (f[i][j] == f[i][j-1]&&j>=1)
		j--;
    if(j==0)j=i;//找到了最前面,那么重置到当前行有效的第一个
	return j;
}
int main()
{
	int m, n; scanf("%d%d", &m, &n);//m花n花瓶
	for (int i = 1; i <= m; i++)
		for (int j = 1; j <= n; j++)
			scanf("%lld", &a[i][j]);
	f[1][1] = a[1][1];
	ll mm = f[1][1];
	for (int j = 2; j <= n; j++) {
		f[1][j] = max(a[1][j], mm);//先对第一行处理,处理后f[i][j]代表的是i-j的最大值
		mm = f[1][j];
	}
	for (int i = 2; i <= m; i++)
	{
		for (int j = 2; j <= n; j++)
			f[i][j] = a[i][j] + f[i - 1][j - 1];//每次都选择能选到的最大值
		mm = f[i][i];//i前面的必定是取不到的直接不管,先将最大值重置为当前行有效的第一个数
		for (int j = i+1; j <= n; j++) {
			f[i][j] = max(f[i][j], mm);//维护最大值
			mm = f[i][j];
		}	
	}
// 	for (int i = 1; i <= m; i++)//输出处理后的数组
// 	{
// 		for (int j = 1; j <= n; j++)
// 			printf("%lld ", f[i][j]);
// 		printf("\n");
// 	}
	printf("%lld\n", f[m][n]);
	//相同的每次都要找到最前面的,再往斜上找相同的第一个
	int cnt = 0;
    int i = m, j = n;//从后往前找
	for (int x = 1; x <= m; x++)//要输出m次
	{
		j=check(i,j);//找到当前相同的数的的最前面的
        z[++cnt]=j;//因为要逆序输出,考虑先存在数组里面
		i--,j--; 
	}
	for (int i = cnt; i >= 1; i--)//逆序输出
		printf("%d ", z[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值