zoukankan      html  css  js  c++  java
  • UVA 11400"Lighting System Design"

    传送门

    错误思路

    正解

    AC代码

    参考资料:

      [1]:https://www.cnblogs.com/Kiraa/p/5510757.html

    题意:

      现给你一套照明系统,这套照明系统共包含 n 种类型的灯;

      其中第 i 中类型的灯包含四个参数:vi,ki,ci,li,分别表示

        vi : 第 i 种类型的灯正常工作所需要的电压源;

        ki : 购买电压源 vi 的花费;

        ci : 第 i 种灯泡的单价;

        li : 这套照明系统需要第 i 种灯的个数

      购买第 i 种灯的花费为 costi = ki+ci×li

      总花费为 cost=∑costi

      有如下操作:

      如果 vi ≤ vj , 那么便可将购买的灯 i 换成购买灯 j;

      求对所有灯执行完上述操作后 cost 的最小值;

    错误思路:

      贪心策略,如果可以将灯 i 换成灯 j 并且可以使得 cost变小,那么就更换,并且更新 li = 0 , lj += li

      hack样例:

    3
    1 100 5 10
    2 150 14 5
    3 300 40 1
    0
      正确输出 690
      实际输出 700

    错误分析:

      初始,cost1=150 , cost2=220 , cost3=340;

      首先可以判断,灯①可以全部换成灯②,换完后,l1=0,l2=15;

      使得 cost1+cost2 = 150+14×15 = 360 < 150+220;

      但是,此时灯②不能全部换成灯③,cost = cost1+cost2+cost3=360+340=700;

      另一种方案是,灯②全部换成灯③,换完后 cost2+cost3=300+40*6=540 < 220+340;

      灯①既不能换成灯②也不能换成灯③,此时,cost = 690 优于 700;

      也就是说,将①换成灯②可以节省10元,但是如果将灯②换成灯③可以节省20元;

      所以,此题的难点就在于对于可以更换的灯 i , j ,是否将灯 i 换成灯 j 呢?

     


    分割线:2019.6.7

      因为要准备考试 6.5 , 6.6 这两天晚上的考试,所以,一直到今天才补了这道题;

      看着紫书上的状态转移方程证明了一下午;

      来看看我今天一下午的成果:

      首先将这 n 种灯按 v 升序排列;

      经过上述操作后,使得总花费最小的方案一定是成片的,什么是成片的呢?

      就是 (1,2,3,...,a-1)(a,...,i,...,b)(b+1,..,j,..,d)(...,n) 这么划分是使得总花费最小的划分方案;

      (a,....,b) : 第 [a,...,b-1] 种灯都换成第 b 种灯;

      为什么这就是答案呢?

      为什么不能是穿插着是答案呢?

      用反证法证明,对于 (a,...,i,...,b)(b+1,..,j,..,d) 这两个划分,假设将 i,j 互换后的划分为使得总花费最小的划分;

      首先定义:

        sum1 = la+la+1+...+li+...+lb;//[a,b]区间的总灯泡个数

        sum2 = lb+1+lb+2+...+lj+...+ld;//[b+1,d]区间的总灯泡个数

        //交换i,j灯泡后的总个数

        sum3 = sum1-li+lj;

        sum4 = sum2-lj+li;

        sum = sum1+sum2 = sum3+sum4;

      ① cost{ (a,...,j,...,b)(b+1,..,i,..,d) } < cost{ (a,...,i,...,b)(b+1,..,j,..,d) };

      ② cost{ (a,...,j,...,b)(b+1,..,i,..,d) } < cost{ (a,............................,d) };

      由①可得:

        (kb+sum3×cb)+(kd+sum4×cd) < (kb+sum1×cb)+(kd+sum2×cd)

      化简得:

        (sum3-sum1)cb < (sum2-sum4)cd

      由②可得:

        (kb+sum3×cb)+(kd+sum4×cd) < kd+sum×cd

      化简得:

        cb < cd;

    正解:  

      首先可以得出这样一个结论:每种类型的灯泡,要么全换成其他类型的,要么全都不换;

      定义dp[ i ]表示前 i 个灯泡得最小花费,sum[ i ]表示前 i 个灯泡得总个数;

      对于两类灯泡 i 和 j,i 可以换成 j 的条件是:

      1) v> vi

      2) j 一定存在于最优解中(保证 k不被省去)

      3) ki+li×ci < lcj

      基于条件(1),首先将灯泡按照电压从小大到大排序;

      假设前 i-1 个灯泡经过最优的替换后,新的灯泡序列为 b1,…,bj,bj+1,…bk(bi表示没被合并的灯泡编号,并且按照电压非降序排列);
      b和 bj+1 的关系一定满足:
        ①kb[ j ]+lb[ j ]×cb[ [j ] < lb[ j ]*cb[ j+1](否则 b可被 bj+1 替换,与当前为最优解的假设矛盾);

      现在我们考虑用第 i 个灯泡 a替换的情况;

      假设 b可以被 a替换,那么有

        ②kb[ j ]+lb[ j ]×cb[ j ] > lb[ j ]×ca[ i ]

      由式 ①② 可得  ca[ i ] < cb[ j+1],即 bj+1 可被 a替换; 

      同理,bj+2~b的所有灯泡都可以被 ai 替换;

      换句话说,对于前 i-1 个灯泡 a1~aj~ai-1,如果存在 j(1<j<i-1),aj+1可以被 ai 替换,

      那么 aj+1~ai-1 的所有灯泡都可以被 ai 替换,而a1~a灯泡的最小费用已经算出为dp[ j ]。

      这样我们就得出了 a的替换方法:前 j 个灯泡用之前计算出的最优方案 d[ j ] 购买,

      剩下 j+1~i 个灯泡全用 a替换,枚举 j 从 0 到 i-1,根据前面的讨论得知不会漏解;

      则状态转移方程 d[ j ]=min{ d[ j ]+c[ i ]×(s[ i ]-s[ j ])+k[ i ] };

      最终答案就是 dp[n];

    AC代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define INF 0x3f3f3f3f
     4 #define memF(a,b,n) for(int i=0;i<=n;a[i++]=b);
     5 const int maxn=1e3+50;
     6 
     7 int n;
     8 struct Data
     9 {
    10     int v,k,c,l;
    11     bool operator < (const Data &obj) const
    12     {
    13         return v < obj.v;
    14     }
    15 }_data[maxn];
    16 int dp[maxn];
    17 int sum[maxn];
    18 
    19 int Solve()
    20 {
    21     sort(_data+1,_data+n+1);
    22     sum[0]=0;
    23     for(int i=1;i <= n;++i)
    24         sum[i]=sum[i-1]+_data[i].l;
    25     memF(dp,INF,n);
    26     dp[0]=0;
    27     for(int i=1;i <= n;++i)
    28         for(int j=0;j < i;++j)
    29             dp[i]=min(dp[i],dp[j]+(sum[i]-sum[j])*_data[i].c+_data[i].k);
    30 
    31     return dp[n];
    32 }
    33 int main()
    34 {
    35     while(~scanf("%d",&n) && n)
    36     {
    37         for(int i=1;i <= n;++i)
    38             scanf("%d%d%d%d",&_data[i].v,&_data[i].k,&_data[i].c,&_data[i].l);
    39         printf("%d
    ",Solve());
    40     }
    41     return 0;
    42 }
    View Code
  • 相关阅读:
    Apache Druid 的集群设计与工作流程
    跨越算法开篇
    十分钟了解Apache Druid(集数据仓库、时间序列、全文检索于一体的存储方案)
    时间序列数据库(TSDB)初识与选择(InfluxDB、OpenTSDB、Druid、Elasticsearch对比)
    C#多线程(6):线程通知
    C#多线程(5):资源池限制
    C#多线程(4):进程同步Mutex类
    C#多线程系列(3):原子操作
    C#多线程系列(2):多线程锁lock和Monitor
    C#多线程系列(1):Thread
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/10978390.html
Copyright © 2011-2022 走看看