UVa #11400 Lighting System Design (例题9-6)

86 篇文章 0 订阅

书中的状态设计一开始不太能理解。为什么换就要全都换?为什么枚举 j 让前 j 个灯泡选取子问题的最优解,而 j+1 到 i 全部换成 i?不可以隔着换吗?


对于第一个问题,起步时可以进行简单的分类讨论来探寻规律:

1、如果灯泡价格比之前贵,那么我们换灯泡本身是亏钱的。只有当全部灯泡都换完,减掉一个电源的时候,我们才盈利。

2、如果灯泡价格比之前还便宜,那么我们为什么不接着享受这个待遇呢?全部换完还能获得电源加成。

3、如果灯泡价格前后一样,那么全部换完获取电源加成是划算的。


因此灯泡要么不换(第一种情况:最后减掉一个电源也亏),要么全换


因此换灯泡就变成了整体换型号的操作。


对于第二个问题,可以用简单的代数求证:

假设存在连续的XYZ,X换Z划算,X换Y不划算,Y换Z不划算。(即产生了隔着换的情况)。注意如果X换Y划算,Y换Z也划算,那么显然X换Z也划算。如果X换Y划算,X换Z、Y换Z都不划算,这种情况则属于求子问题的最优解。

那么:

X换Z划算 => X.k + X.l * (Z.c - X.c) > 0

X换Y不划算 => X.k + X.l * (Y.c - X.c) < 0

因为数据都是正整数,综合两式可得 Z.c > Y.c

Y换Z是否划算 => Y.k + Y.l * (Z.c - Y.c) ?> 0

因为Z.c > Y.c,所以上式恒大于0,因此Y换Z一定也是划算的。所以最优解永远不会包含“隔着换”的情况。


这样算法的正确性就可以基本确定了。如果不是有书中的提示,估计自己很难独立做出来。


记忆化搜索比递推法慢很多,个人认为主要因为memset和递归函数调用。


递推:Run Time: 0.143

#define UVa  "LT9-6.11400.cpp"		//Lighting System Design
char fileIn[30] = UVa, fileOut[30] = UVa;

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>

using namespace std;

struct Bulb {
    int v, k, c, l;
    Bulb(){v = k = c = l = 0;}
    Bulb(int* a){
        v = a[0]; k = a[1]; c = a[2]; l = a[3];
    }
    bool operator < (const Bulb& b) const {
        return v < b.v;
    }
};

//Global Variables. Reset upon Each Case!
const int maxn = 1000 + 10;
int n;
Bulb bulbs[maxn];
int d[maxn];
int s[maxn];
/

int main() {
    while(scanf("%d", &n) && n) {
        for(int i = 0; i < n; i ++) scanf("%d%d%d%d", &bulbs[i].v, &bulbs[i].k, &bulbs[i].c, &bulbs[i].l);
        sort(bulbs, bulbs + n);
        int cnt = 0;
        for(int i = 0; i < n; i ++) {
            cnt += bulbs[i].l;
            s[i] = cnt;
        }
        for(int i = 0; i < n; i ++) {
            d[i] = s[i]*bulbs[i].c + bulbs[i].k;
            for(int j = 0; j < i; j ++)
                d[i] = min(d[i], d[j] + (s[i] - s[j])*bulbs[i].c + bulbs[i].k);
        }
        printf("%d\n", d[n-1]);
    }
    return 0;
}


记忆化搜索:0.362s

#define UVa  "LT9-6.11400.cpp"		//Lighting System Design
char fileIn[30] = UVa, fileOut[30] = UVa;

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>

using namespace std;

struct Bulb {
    int v, k, c, l;
    Bulb(){v = k = c = l = 0;}
    Bulb(int* a){
        v = a[0]; k = a[1]; c = a[2]; l = a[3];
    }
    bool operator < (const Bulb& b) const {
        return v < b.v;
    }
};

//Global Variables. Reset upon Each Case!
const int maxn = 1000 + 10;
int n;
Bulb bulbs[maxn];
int d[maxn];
int s[maxn];
/

int dp(int i) {
    if(d[i]) return d[i];
    d[i] = s[i]*bulbs[i].c + bulbs[i].k;
    for(int j = 0; j < i; j ++)
        d[i] = min(d[i], dp(j) + (s[i] - s[j])*bulbs[i].c + bulbs[i].k);
    return d[i];
}

int main() {
    while(scanf("%d", &n) && n) {
        for(int i = 0; i < n; i ++) scanf("%d%d%d%d", &bulbs[i].v, &bulbs[i].k, &bulbs[i].c, &bulbs[i].l);
        sort(bulbs, bulbs + n);
        memset(d, 0, sizeof(d));
        int cnt = 0;
        for(int i = 0; i < n; i ++) {
            cnt += bulbs[i].l;
            s[i] = cnt;
        }
        printf("%d\n", dp(n-1));
    }
    return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值