题目大意:
给定N组灯具,每组灯具包含V(灯的电压),P(电源花费),C(灯的单价),N(所需要的灯的数量)。规定低电压的灯可以换成高电压的灯,同时电源也要换。且一个电源可供无数灯运作。要求部署N组灯具的最小花费。
思路:
首先按照电压V从小到大进行排序,这样才能确定递推顺序。其次是状态转移,这里用到的是区间dp的套路即区间【L,R】由【L,i】【i+1,R】转移而来。我想过这个问题,就是能不能连续选择一段区间的问题,最初是没有证明出来的,所以也就没用这种方法。比如1,2,3。为什么不能选1,3为一组,2为一组。 仔细想想d[i]的方程表达式其意义是前i组灯具的最小花费。如果是前两组答案只能是1,2;2,2;这两种方法。如果选1,3为一组,2为一组。则2的花费肯定很大,此时就可以把2替换成3,所以上边那种解是不存在的,所以不存在漏解的情况。
所以状态转移方程:
Dp[i]=min(dp[i],(s[i]-s[j])*cost[i]+dp[j]+pow[i])
反思:
在此题中,每个阶段的决策不清晰会导致想不出状态转移方程,比如此题替换只能连续替换,决策没有很复杂。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1e3+10;
const int INF=99999999;
struct terms{
int v,power_cast,lamp_cast,num;
};
bool cmp(terms a,terms b){
return a.v<b.v;
}
terms box[MAXN];
int dp[MAXN];
int s[MAXN];
int main()
{
//freopen("1.txt","r",stdin);
//freopen("2.txt","w",stdout);
int n;
while(scanf("%d",&n)&&n){
memset(s,0,sizeof(s));
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&box[i].v,&box[i].power_cast,&box[i].lamp_cast,&box[i].num);
}
sort(box+1,box+1+n,cmp);
for(int i=1;i<=n;i++){
dp[i]=INF;
s[i]=s[i-1]+box[i].num;
for(int j=0;j<i;j++){
dp[i]=min(dp[i],dp[j]+(s[i]-s[j])*box[i].lamp_cast+box[i].power_cast);
}
}
printf("%d\n",dp[n]);
}
}