题意:有 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;
}