POJ 1260 Pearls 解析

题目链接

题意:有 c (1<=c<=100) 个不同档次的珍珠,价格随着档次升高而升高,为了防止顾客只买1 个珍珠,现规定顾客必须加付相当于所买档次10个珍珠的钱。我们发现,有时候将等级低的珍珠用等级高的珍珠来替代购买,反而能省钱,因为此时每个珍珠因为档次升高而多付的钱少于本来需要加付的钱。问,给定各个等级需要买的珍珠数目和该等级每颗珍珠的价钱,怎样的替代方案才能最省钱?

错误思路:贪心。从等级最低的开始,判断将其全部用高1等的珍珠来替代是否划算,是则替换并接着试着向上合并,不是则购买这一等级的珍珠。

//num[i] 表示需购买的第i等级的珍珠的数量
//price[i] 表示每颗第i等级的珍珠的价格
// c 表示总的等级数 
for( i = 1; i < c; i ++ ){
		if( ( num[i] + 10 ) * price[i] + ( num[i + 1] + 10 ) * price[i + 1] >= ( num[i] + num[i + 1] + 10 ) * price[i + 1] )
			num[i + 1] = num[i] + num[i + 1];
		else
			sum += ( num[i] + 10 ) * price[i];
}
sum += ( num[c] + 10 ) * price[c];
printf( "%d\n", sum );

上述思路的错误之处在于 即使某一档次的珍珠在第一次向上合并时是划算的,但并不代表接下来的向上合并过程中也如此。

比如:number       price

 等级一      9   1

等级二      1   2

等级三       1  3

等级一的 9 颗珍珠在第一次合并到等级二的时候是合算的,因为 10 * 1 > ( 2 - 1 ) * 9 , 但当等级一二的珍珠合并后,再和等级三的珍珠合并时,虽然此时表面上看是合算的,因为  10 * 2  > ( 9 + 1 ) * ( 3 - 2 ) ,实际上,这时等级一的 9 颗珍珠已不划算了,因为此时 10 * 1 < 9 * ( 3 - 1 ) ,故此例的最佳方案并不是贪心法得出的一二都用三来代替最终price = 63  的方案,而是一不变,二用三代替最终 price = 55 的方案。

正确思路一:此题有一个特殊之处,即合并一定是连续的,因为若 i 跳过 i - 1 和 小于 i - 1等级的珍珠合并,则必有一个更好的替代方案是 该小于 i - 1等级的珍珠和i - 1 合并,此方案一定更省钱。

ans[ i ][ j ] 表示购买等级 i 到等级 j 的珍珠所需钱数。 对 j - i 从 0 到 n - 1 进行动态规划。

  ans[ i ][ j ] = min{ ans[ i ][ k ] + ans[ k +1 ][ j]  ,i  <=  k < j }

#include <iostream>
using namespace std ;
#define MAX 103
int p[MAX];     //p[i] 表示等级为 i 的珍珠每颗所需价钱
int sum[MAX] ;  //sum[i] 表示从等级1 到等级i 所需购买的珍珠数量总和
int ans[MAX][MAX] ;  //ans[ i ][ j ] 表示购买等级 i 到等级 j 的珍珠所需钱数
int main()
{
	int t ;
	cin >> t ;
	while ( t-- )
	{
		int i , j , n , r ;
		cin >> n ;
		sum[0] = 0 ;
		for ( i = 1 ; i <= n ; i ++ )
		{
			cin >> sum[i] >> p[i] ;
			sum[i] = sum[i-1] + sum[i] ;
		}

		对 j - i 从 0 到 n - 1 进行动态规划。
		for ( r = 0 ; r < n ; r++ )
		{
			for ( i = 1 ; i <= n-r ; i++ )
			{
				j = i + r ;
				ans[i][j] = ( sum[j] - sum[i - 1] + 10 ) * p[j] ;  注意不能少

				ans[ i ][ j ] = min{ ans[ i ][ k ] + ans[ k +1 ][ j]  ,i  <=  k < j }
				int k ;
				for ( k = i ; k < j ; k++ )
				{
					int temp = ans[i][k] + ans[k + 1][j] ;
					if ( temp < ans[i][j] )
						ans[i][j] = temp ;
				}
			}
		}
		cout << ans[1][n] << endl ;
	}
	return 0 ;
}

正确思路二:思路一的动归可以退化成一维的动归:dp[ i ] 表示购买从等级1 到等级 i  所有的珍珠所需的最少钱数。

   dp[ i ] = min{ dp[ k ] + ( sum[ i ] - sum[ k ] + 10 ) * price[ i ] , k <= i },其中sum[i] 表示从等级1 到等级i 所需购买的珍珠数量总和

#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
#define MAX 100
int dp[MAX + 5];     //dp[ i ] 表示购买从等级1 到等级 i  所有的珍珠所需的最少钱数
int price[MAX + 5];  //price[i] 表示等级为 i 的珍珠每颗所需价钱
int sum[MAX + 5];	 //sum[i] 表示从等级1 到等级i 所需购买的珍珠数量总和
int main()
{
	int  i, j, t, c;
	scanf("%d", &t);
	while( t -- ){		
		memset( dp, 0x7f, sizeof(dp) );
		scanf("%d", &c);
		for( i = 1; i <= c; i ++ ){
			scanf("%d%d", &sum[i], &price[i]);
			sum[i] = sum[i - 1] + sum[i];
		}
		dp[0] = 0;

		//dp[ i ] = min{ dp[ k ] + ( sum[ i ] - sum[ k ] + 10 ) * price[ i ] , k <= i }
		for( i = 1; i <= c; i ++ ){
			for( j = 1; j <= i; j ++ ){
				if( dp[i] > dp[i - j] + ( sum[i] - sum[i - j] + 10 ) * price[i] )
					dp[i] = dp[i - j] + ( sum[i] - sum[i - j] + 10 ) * price[i];
			}
		}
	    printf( "%d\n", dp[c] );
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值