POJ1260-Pearls【贪心的错误与dp的思路】

题目大意

大致题意:

给出几类珍珠,以及它们的单价,要求用最少的钱就可以买到相同数量的,相同(或更高)质量的珍珠。

【规定买任一类的珍珠 n n n个(价格为 p p p),都要支付 ( n + 10 ) ∗ p (n+10)*p (n+10)p的钱,即额外支付 10 ∗ p 10*p 10p

样例分析

例如样例Input的第二个例子:

3

1 10

1 11

100 12

需要买第一类1个,第二类1个,第三类100个

按常规支付为 (1+10)*10 + (1+10)*11 + (100+10)*12 = 1551元(一共买了102个珍珠)

但是如果全部都按照第三类珍珠的价格支付,同样是买102个,而且其中总体质量还被提高了,但是价格却下降了:(102+10)*12 = 1344元

而对于样例Input的第一个例子:

2

100 1

100 2

按常规支付为 (100+10)*1 + (100+10)*2 =330元

但是全部按第二类珍珠的价格支付,同样买200个,虽然总体质量提升了,但是价格也提高了: (202+10)*2=424元

贪心为什么错

一开始想过从头到尾遍历珍珠的价格和数目,每次都看相临的两个,如果把当前这一批放入下一个等级价格降低,则将他放到下一个等级,否则直接算在当前等级的买的价格加到答案中,最后一等后设置最大值,保证不会再向后放.,但是这样的思路其实是错误的,以下是证明:

价格 p a < p b < p c pa<pb<pc pa<pb<pc
假设贪心得出 ( a + 10 ) p a > a ∗ p b (a+10)pa>a*pb (a+10)pa>apb ( a + b + 10 ) p b > ( a + b ) ∗ p c (a+b+10)pb>(a+b)*pc (a+b+10)pb>(a+b)pc两个前提
但是对于 ( a + 10 ) ∗ p a + ( b + c + 10 ) ∗ p c < ( a + b + c + 10 ) ∗ p c (a+10)*pa+(b+c+10)*pc<(a+b+c+10)*pc (a+10)pa+(b+c+10)pc<(a+b+c+10)pc即不采取归并更小的情况是否存在呢
化简式子:
( a + 10 ) ∗ p a < ( a + b ) ∗ p c (a+10)*pa<(a+b)*pc (a+10)pa<(a+b)pc

对比条件,我们知道 ( a + 10 ) ∗ p a > a ∗ p b 和 ( a + b + 10 ) p b > ( a + b ) ∗ p c (a+10)*pa>a*pb和(a+b+10)pb>(a+b)*pc (a+10)pa>apb(a+b+10)pb>(a+b)pc

完全可以有 a ∗ p b < ( a + 10 ) ∗ p a < ( a + b ) ∗ p c < ( a + b + 10 ) ∗ p b a*pb<(a+10)*pa<(a+b)*pc<(a+b+10)*pb apb<(a+10)pa<(a+b)pc<(a+b+10)pb

所以贪心策略是错的

为什么可以dp以及dp的证明

首先证明最优解中肯定不会有交叉的替换,即在质量为a<b<j<c<d的情况下,如果a被c替换,那么b也一定要被c替换 假设在最优解中存在:a被c替换且b不被c替换的情况:

1.b不被任何替换 那么此时把a换成用b替换 得到比原来更优的解,错误
2.b被j替换,此时把a换成用j替换 得到比原来更优的解,错误
3.b被d替换,此时把b换成用c替换 得到比原来更优的解,错误

因此,不存在交叉的替换,那么最优解分为下面两种情况:
1.不存在断点,即所有的都用最大一种替换, d p [ c ] = ( a [ 1 ] + . . . + a [ c ] + 10 ) ∗ p [ c ] dp[c]=(a[1]+...+a[c]+10)*p[c] dp[c]=(a[1]+...+a[c]+10)p[c]
2.存在断点,最后一个断点为i,那么最优解必为 d p [ i ] + ( a [ i + 1 ] + . . + a [ c ] + 10 ) ∗ p [ c ] dp[i]+(a[i+1]+..+a[c]+10)*p[c] dp[i]+(a[i+1]+..+a[c]+10)p[c]

每个点都有可能是最后一个断点,因此产生了我们的DP方程:
d p [ i ] = m i n ( d p [ j ] + ( a [ j + 1 ] + . . + a [ i ] + 10 ) ∗ p [ i ] ) dp[i]=min(dp[j]+(a[j+1]+..+a[i]+10)*p[i]) dp[i]=min(dp[j]+(a[j+1]+..+a[i]+10)p[i]
把dp[0]设成0的话 2就包含了1 dp[c]就是最后的答案

有点像最长上升子序列的问题

int T, N, a[MAX], p[MAX], dp[MAX];
int main() {
	cin >> T;
	while (T--) {
		cin >> N;
		for (int i = 1; i <= N; i ++ )cin >> a[i] >> p[i];
		memset(dp, 0, sizeof(dp));
		for (int i = 1; i <= N; i++) {
			int cnt = inf, sum = 0;
			for (int j = i; j > 0; j--) {
				sum += a[j];
				cnt = min(cnt, dp[j - 1] + (sum + 10)*p[i]);
			}
			dp[i] = cnt;
		}
		cout << dp[N] << endl;
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值