一. Vjudge链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=26154
二. 题目大意:给出灯泡的4个属性,电压,所需电源的花费,每个灯泡所需的花费,所需要灯泡的数量,并且不同的灯泡需要不同的电源,电压低的灯泡可以被电压高的灯泡换掉。求最小的花费。
三. 思路:存在最小花费的原因:一是把一种灯泡全部换掉了,然后节省了一个电源的花费。二是由于灯泡的单个花费比较大,用一个单个花费少的灯泡把它换掉。而第二种情况,如果能换1个,那么全部换了就会更省钱,因此存在最小花费只有一种情况,一种灯泡被另一种全部换掉,若干次换,或者不换,取得了最优解。考虑只有2个的情况,那么有2种可能,各自用自己的电源,或者电压大的灯泡替代电压小的灯泡,之中取最小的。而再加入一个电压更大的灯泡,看起来有2^(3-1),共4种情况,而实际上如果电压第二大的替代了电压最小的,那么就只要考虑电压最大的会不会替代前面的2个灯泡就行了。如果电压第二大的没有替代电压最小的,那么还是只要考虑电压最大的会不会替代前面的最优解,或者它不替代就形成了当前的最优解。先将灯泡按电压大小排序,让前面的灯泡变得可以被后面的灯泡替代。
于是动态转移方程就出来了:dp[i] = min(dp[j] + (sum[i]-sum[j])*num[i] + supply) dp[i]记录前i个的最优解,sum[i]记录前i个灯泡总数,num[i]记录第i个灯泡的数量,supply表示电源花费。j严格小于i。表示要得到当前的最优解,只要寻找前面的所有最优解加上被替代的灯泡的花费。也就是j之后的灯泡都是被i代替了。
而实际上,这个模型和最长上升子序列有相似之处,最长上升子序列求法也是扫一遍前面所有的最优解,取(有符合条件的最优解+1,或者不符合条件最优解就保留长度)的最大值,赋值给当前值。
四. 代码:
#include <iostream> #include <cstdio> #include <queue> #include <cstring> #include <cmath> #include <algorithm> #include <stack> using namespace std; const int MAX_N = 1025, INF = 0x3f3f3f3f; struct Light { int vol, supply, cost, num; }light[MAX_N]; int dp[MAX_N], lightSum, sum[MAX_N]; bool cmp(Light a, Light b) { return a.vol < b.vol; } int main() { //freopen("in.txt", "r", stdin); int i, j; while(~scanf("%d", &lightSum) && lightSum){ for(i = 1; i <= lightSum; i++) scanf("%d%d%d%d", &light[i].vol, &light[i].supply, &light[i].cost, &light[i].num); sort(light + 1, light + lightSum + 1, cmp); dp[0] = 0; sum[0] = 0; for(i = 1; i <= lightSum; i++) sum[i] = light[i].num, sum[i] += sum[i-1]; for(i = 1; i <= lightSum; i++){ int tmp = INF; for(j = 0; j < i; j++) tmp = min(tmp, dp[j] + (sum[i]-sum[j])*light[i].cost + light[i].supply); dp[i] = tmp; } printf("%d\n", dp[lightSum]); } return 0; }