POJ1260 Pearls(动态规划求最小花费)

题目描述

在珍珠岛,珍珠被分为100个不同的等级。一个等级是由该等级的一颗珍珠的价格来确定的。这个价格对该等级来说是唯一的,而且这个价格总是高于低等级珍珠的价格。

每个月,库存经理都会准备一份清单,列出每个等级所需的珍珠数量。每个等级的珍珠都有自己的价格,但每完成一个特定等级的交易,就必须额外支付相当于该等级10颗珍珠的钱。这是为了防止游客只买一颗珍珠。

库存经理发现,他有时可以通过购买比实际需要的更高质量的珍珠来节省资金。在相同或更低的价格买到更高品质的珍珠,当然是一件欢喜的事情。

例如,在 10 10 10 欧元的类别中需要 5 5 5 颗珍珠,在 20 20 20 欧元的类别中需要 100 100 100 颗珍珠。这通常需要花费: ( 5 + 10 ) ∗ 10 + ( 100 + 10 ) ∗ 20 = 2350 (5+10)* 10+(100+10)* 20=2350 (5+10)10+(100+10)20=2350 欧元。购买 20 20 20 欧元类别的所有 105 105 105 颗珍珠只需要花费: ( 5 + 100 + 10 ) ∗ 20 = 2300 (5+100+10)*20=2300 (5+100+10)20=2300 欧元。

问题是库存经理想实现这样的操作,需要进行大量的计算工作。你被要求用一个计算机程序来帮助库存经理。

给出一个包含珍珠数量和不同等级的每颗珍珠价格的清单,给出购买清单上所有珍珠所需的最低价格。珍珠可以按要求购买,也可以按更高的质量等级购买,但不能按更低的等级购买。

输入格式

输入的第一行为测试案例的数量。

每个测试案例都以包含类别数量 c c c 的一行开始 ( 1 ≤ c ≤ 100 ) (1\leq c\leq 100) (1c100)

接下来是 c c c 行,每行有两个数字 a i a_i ai p i p_i pi。其中,第一个数字为一个类别中需要的珍珠数量 a i a_i ai ( 1 ≤ a i ≤ 1000 ) (1\leq a_i\leq 1000) (1ai1000)。第二个数字是该等级中每颗珍珠的价格 p i p_i pi ( 1 ≤ p i ≤ 1000 ) (1 \leq pi \leq 1000) (1pi1000)

各个等级的价格是按升序排列的。输入的所有数字都是整数。

输出格式

对于每个测试案例,输出一个整数,表示购买清单上所有东西所需的最低价格。

样例输入

2
2
100 1
100 2
3
1 10
1 11
100 12

样例输出

330
1344

提交链接

http://poj.org/problem?id=1260

提示

对于

3
1 10
1 11
100 12

正常花费: ( 1 + 10 ) ∗ 10 + ( 1 + 11 ) ∗ 10 + ( 100 + 10 ) ∗ 12 = 1551 (1+10)*10+(1+11)*10+(100+10)*12=1551 (1+10)10+(1+11)10+(100+10)12=1551,花费 1551 1551 1551
若把第一种归到第二种: ( 2 + 10 ) ∗ 11 + ( 100 + 10 ) ∗ 12 = 1452 (2+10)*11+(100+10)*12=1452 (2+10)11+(100+10)12=1452,花费 1452 1452 1452
若把第一种,第二种归到第三种: ( 102 + 10 ) ∗ 12 = 1344 (102+10)*12=1344 (102+10)12=1344,花费 1344 1344 1344

所以正确的选择是第三种的购买 102 102 102 颗,花费的钱少,品质也更高。

解析

通过上面可以看出,求组合的每一种状态的最小值。
dp[i]:购买前 i i i(包括 i i i)种商品,所花费的最少金额。

1.初始化:刚开始的状态,是各买各的,有 i i i 类珍珠,就分 i i i 次购买

for(int i=1; i<=c; i++)
{
	cin>>a[i]>>p[i];               //数量 价格
	sum[i]=sum[i-1]+a[i];
	dp[i]=dp[i-1]+(a[i]+10)*p[i];  //初始状态
}

2.然后通过状态迁移,找出最小的。

for(int i=1; i<=c; i++)           //状态转移
{
	for(int j=0; j<i; j++)
		dp[i]=min(dp[i],dp[j]+(sum[i]-sum[j]+10)*p[i]);
}

参考代码

#include<bits/stdc++.h>
using namespace std;
int a[102],p[102],sum[102],dp[102];
int c,t;
int main()
{
	cin>>t;  //t组样例
	while(t--)
	{
		memset(sum,0,sizeof(sum));   //前i类珍珠的数量总和
		memset(dp,0,sizeof(dp));    //购买前i(包括i)种商品,所花费的最少金额
		cin>>c;                    //c种珍珠
		for(int i=1; i<=c; i++)
		{
			cin>>a[i]>>p[i];               //数量 价格
			sum[i]=sum[i-1]+a[i];
			dp[i]=dp[i-1]+(a[i]+10)*p[i];  //初始状态
		}
		for(int i=1; i<=c; i++)           //状态转移
		{
			for(int j=0; j<i; j++)
				dp[i]=min(dp[i],dp[j]+(sum[i]-sum[j]+10)*p[i]);
		}
		cout<<dp[c]<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zaiyang遇见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值