题解 UVA11400 【照明系统设计 Lighting System Design】
状态的定义:按额定电压排序后,dp[i]:表示前i个物品,以第i个物品结尾的最小费用
状态的转移:dp[i]这个状态由dp[j]转移而来,其中j+1到i的物品都被替换成第i个物品。
时间复杂度:O(N^2)
看了一些题解,好像都没说为什么"j+1到i的物品都被替换成第i个物品"这样做是合理的。
当然我也想了一个下午才明白了,下面大致说一下:
假设排序后物品序号如下:1,... ,i,i+1,i+2,...
假如我们在更新这个方程
dp[i+2] = std::min(dp[i+2], dp[i-1] + {i~i+2都被替换成第i+2个物品的花销})
此时一个直观的疑问是:问什么i,i+1,i+2都被替换成i+2这个物品,难道不可以只是第i个物品被替换成i+2这个物品,而第i+1个物品保持原样即不被i+2这个物品替换。
大家可以想象下,第i个物品和第i+2个物品很昂贵,而第i+1个物品很便宜。如下面这组数据:
//额定电压,电压源,每个灯泡的费用,数量
第i个物品:10,300,100,100
第i+1个物品:100,1,1,1
第i+2个物品:200,300,100,100
这样的话i,i+1,i+2都被替换成i+2,此时要:300+(100)*(100+1+100)
而i, i+2都被替换成i+2,而i+1保持不变,此时要:300+ 100*(100+100) + 1+1x1
此时发现后一种比前一种还要便宜,是不是我们将连续的一段如i,i+1,i+2都替换成i+2是错误的呢?
其实不是
大家接着思考一下,我们在更新dp[i+2]的时候,是不是dp[i+1]已经被更新过了,在更新dp[i+1]的时候已经把第i个物品替换成i+1这个物品了
此时虽然dp[i+2]从 {dp[i-1]这个状态 + 将i到i+2都替换成i+2这个物品}转移过来不是最优的,但是dp[i+2]也可以从dp[i+1]这个状态转移过来呀。
所以dp[i]在状态转移的时候,将连续的一段都替换成第i个物品是合理的,它不会丢失最优解。
//
// created by **** on 2019-08-17
//
#include <bits/stdc++.h>
const int maxn = 1e3 + 10;
int n;
// 按额定电压排序后,dp[i]:表示前i个物品,以第i个物品结尾的最小费用
// 因为前i个物品中,排序后第i个物品的额定电压最大,所以它是不能被其他物品替换的,所以第i个物品
// 一定是结尾,这里只是显示的定义出来。
int dp[maxn],sum_[maxn];
struct node{
// 额定电压,电压源,费用,数量
int voltage_rating;
int voltage_source;
int cost;
int number;
// 额定电压从小到大排序
const bool operator< (const node& rhs) const{
return voltage_rating < rhs.voltage_rating;
}
}A[maxn];
int main(){
while(scanf("%d",&n) && n){
memset(sum_, 0, sizeof sum_);
memset(dp, 0x3f3f3f, sizeof dp);
dp[0] = 0;
for(int i=1;i<=n;++i){
scanf("%d %d %d %d",&A[i].voltage_rating, &A[i].voltage_source, &A[i].cost, &A[i].number);
}
// 排序以及前缀和
std::sort(A+1,A+1+n);
for(int i=1;i<=n;++i){
sum_[i] = sum_[i-1] + A[i].number;
}
// dp过程
for(int i=1;i<=n;++i){
for(int j=0;j<i;++j){
dp[i] = std::min(dp[i], dp[j] + (sum_[i]-sum_[j])*A[i].cost + A[i].voltage_source);
}
}
printf("%d\n",dp[n]);
}
return 0;
}