• 关于贪心的一些妙题 (更新中...)


     

    [USACO13FEB] 出租车Taxi

    题意

    长度为M的栅栏上,有N(1 <= N <= 100,000)头牛需要坐车前往别的地方,起点和终点分别为Ai和Bi。现在一辆出租车从最左端0出发,要运送完所有牛,最后到达最右端M(1 <= M <= 1,000,000,000),求最小路程。
    出租车只能一次载一只牛。奶牛可以在同一时刻完成上车和下车。

    输入

    第1行:输入N和M; 

    第2~N+1行:每行2个数,第i+1行,表示第i头牛起点Ai和终点Bi (0≤Ai, Bi≤M)

     
    输出
    输出1行,表示行程的最小值
     
    输入 #1
    2 10 
    0 9 
    6 5 
    
    输出 #1
    12 


    首先,这是一道妙题。(笔者“妙题”的定义 是 自己想不出来的题)
    同桌在写这道题,我就跟了一波风。他说,这题很暴力的写就行了。
    看了题,我的第一反应是,噢~差不多知道怎么写DP了。
    然后我就这么说了,一波群嘲,被告知贪心就可以了。贪心?...我开始了苦想贪心的过程...
    最终...看了题解...


    思路:
      1. 总路程=载牛路程+空载路程

    (1) 由于 一次只能载一只牛不走回头路(从Ai直达Bi) 保证了 载牛路程min= ∑ abs(Ai-Bi)
    (2) 回头次数min回头距离min 保证了 空载路程min

    •   什么时候会空载?

    从初始点(Ai)运送"一个"奶牛的途中,遇到了 第一个(最近的) 目的点——

     

    此时的目的点有两种情况。

     

      a. 这个目的点 是 "另一个"奶牛的目的点(Bj) :

    此时,由于一次只能载一只牛,为了答案更优,需此(Bj)放下这"一个"奶牛,而载上"另一个"奶牛,先送"另一个"去目的(Bj)由于第(1)步里,我们已经算了每个奶牛从起始点到目的点的距离和,所以已经加过"另一个"奶牛从起始点(Aj)目的点(Bj)的距离

    所以,我们只需加上 "另一个"的目的点(Bj) 返回 放下那"一个"的原地点(Aj)的路程 =回头路=空载路

     

      b. 这个目的点 就是 这"一个" 的目的点 (Bi)

    显然,将这"一个"送达目的点后 到 "另一个"(下一个)奶牛的起始点之间也是空载路

     

      c. 注意: 从0到m,首尾是有两段空载路的。

     

    • 说到首尾,有一种巧妙的处理方法 (咕咕咕)

     

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define N 100010
     4 using namespace std;
     5 int read(){
     6     int x=0,f=1; char c=getchar();
     7     while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
     8     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
     9     return x*f;
    10 }
    11 int n,m;
    12 long long ans;
    13 int a[N],b[N];
    14 int main()
    15 {
    16     n=read(),m=read();
    17     for(int i=1;i<=n;i++){
    18         a[i]=read(),b[i]=read();
    19         ans+=abs(a[i]-b[i]);
    20     }
    21     n+=1;
    22     a[n]=m,b[n]=0;
    23     sort(a+1,a+1+n);
    24     sort(b+1,b+1+n);
    25     for(int i=1;i<=n;i++)
    26         ans+=abs(a[i]-b[i]);
    27     printf("%lld",ans);
    28     return 0;
    29 }

     
    P2512 [HAOI2008]糖果传递

    题意:有n个小朋友坐成一圈,每人有ai个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为1。

          求使所有人获得均等糖果的最小代价。

    思路:

      1. 初始 a [ i ] 颗糖果,X [ i ] 表示第i个小朋友给了第i-1个小朋友Xi颗糖果。 如果Xi<0,说明第i-1个小朋友给了第i个小朋友Xi颗糖果,X1表示第一个小朋友给第n个小朋友的糖果数量。

                所以最后的答案就是 ans= |X1| + |X2| + |X3| + ……+ |Xn|。

          对于第一个小朋友,他给了第n个小朋友X1颗糖果,还剩a1-X1颗糖果,但因为第2个小朋友给了他X2颗糖果,所以最后还剩a1-X1+X2颗糖果。根据题意,最后的糖果数量等于average,即得到了一个方程:a1-X1+X2=average

          同理,对第二个人有 a2-x2+x3=average。于是我们可以得到n个方程,共n个变量。

          但我们却不能直接解方程。因为从前n-1个方程可以推出最后一个。实际上有用的只有n-1个。

    对于第1个小朋友,a1-X1+X2=average   ->   X2=average-a1+X1 = X1 - b1  (假设 b1=a1-average,下面类似)

    对于第2个小朋友,a2-X2+X3=average      ->   X3=average-a2+X2 = 2*average-a1-a2+X1 = X1- b2  ( b2=a1+a2-2*average)

    对于第3个小朋友,a3-X3+X4=average   ->   X4=average-a3+X3 = 3*average-a1-a2-a3+X1 = X1- b3

    ......

    对于第n个小朋友,an-Xn+X1=average  

    由于可以用X1表示出其他的Xi,那么本题就变成了单变量的极值问题。

      2. 我们希望Xi的绝对值之和尽量小,即|X1| + |X1-C1| + |X1-C2| + ……+ |X1-Cn-1|要尽量小。

          注意到 |X1-Ci| 的几何意义是数轴上的点X1到Ci的距离,所以问题变成了:给定数轴上的n个点,找出一个到他们的距离之和尽量小的点。

          不难猜到,最优的 Xi 就是这些数中的中位数。

          接下来,我们证明:

           数轴上n个点,中位数到各点的距离之和最小

      Eg:

      拿图中D点举例。
      D点左边有3个点,右边有一个。
      当D向左平移d(未接触C)时,左边各点与它的距离都减少了d,右边各点与它的距离都增加了d。在D上的话,距离之和减少了2d。
      所以,只有在最中间的点满足条件使其到各个顶点距离之和最少。
    code:
     1 #include<cstdio>
     2 #include<algorithm>
     3 #define ll long long
     4 using namespace std;
     5 int read()
     6 {
     7     int x=0,f=1; char c=getchar();
     8     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
     9     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    10     return x*f;
    11 }
    12 ll n,mid,sum,ans,average;    //注意 long long 
    13 int a[1000010],b[1000010];
    14 int main()
    15 {
    16     n=read();
    17     for(int i=1;i<=n;i++){
    18         a[i]=read();
    19         sum+=a[i];
    20         
    21     }
    22     average=sum/n;
    23     for(int i=1;i<=n;i++) b[i]=b[i-1]+a[i]-average;
    24     sort(b+1,b+1+n);
    25     mid=b[(n+1)/2];
    26     for(int i=1;i<=n;i++)
    27         ans+=abs(mid-b[i]);
    28     printf("%lld",ans);
    29     return 0;
    30 }

     
    P3049 [USACO12MAR]园林绿化Landscaping

    题目描述

    花园由N个花圃(1≤N≤100,000)组成, 第i个花圃最开始有Ai个泥土,使每个花圃最后有Bi个泥土。(Ai和Bi都是0~10范围内的整数) 
    有几个选择:购买一块的土,并将它放在他选择的花圃中, 花费X单位的钱。  
          清除一块泥土,花费Y单位的钱。
          他还可以用 Z*|i-j| 的花费将一单位的泥土从花圃 i 运输到花圃 j 。 
          (0≤X,Y≤108, 0≤Z≤1000)
    请计算农民约翰完成他的绿化项目的最低总成本。 
     

    FA 1  DP ( 两边似乎还有些问题,咕一下...)

    思路:

      1. 把输入的初始数组和目标数组转化成如下格式:

         a  2 3 4 5

          a' 1 1 2 2 2 3 3 3 3 4 4 4 4 4

          b  3 1 5 2

          b' 1 1 1 2 3 3 3 3 3 4 4

      2. f [ i ][ j ] 中 i 表示运走土块数, j 表示购买土块数

    code:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,x,y,z,a[1010],b[1010],f[1010][1010],p,q;
     4 int minx(int a,int b,int c)
     5 {
     6     if(a<=b &&a<=c)return a;
     7     if(b<=a &&b<=c)return b;
     8     if(c<=a &&c<=b)return c;
     9 }
    10 
    11 int main()
    12 {
    13     int lena=0,lenb=0;
    14     cin>>n>>x>>y>>z;
    15     for(int i=1;i<=n;i++){
    16     cin>>p>>q;
    17     for(int j=1;j<=p;j++)a[++lena]=i;
    18     for(int k=1;k<=q;k++)b[++lenb]=i;
    19     }
    20     f[0][0]=0;     //极限情况作为边界
    21     for(int i=1;i<=lena;i++)f[i][0]=i*y;
    22     for(int i=1;i<=lenb;i++)f[0][i]=i*x;
    23 
    24     for(int i=1;i<=lena;i++){
    25       for(int j=1;j<=lenb;j++){
    26         f[i][j]=minx(f[i-1][j]+y,f[i][j-1]+x,f[i-1][j-1]+z*abs(a[i]-b[j]));
    27       }
    28       }
    29     cout<<f[lena][lenb]<<endl;
    30     return 0;
    31 }

    FA  2  堆贪心

  • 相关阅读:
    HDU5877
    HDU5874
    HDU5875
    广西党史知识竞赛活动
    知识竞答小程序更新记录
    答题小程序批量导入时增加对图片的支持
    关注】答题赢话费,安全用妆知识竞赛小程序上线啦!
    反向代理应知应会
    抽奖助手小程序v3
    答题小程序功能列表
  • 原文地址:https://www.cnblogs.com/RR-Jin/p/11639005.html
Copyright © 2020-2023  润新知