zoukankan      html  css  js  c++  java
  • DP+贪心水题合集_C++

    本文含有原创题,涉及版权利益问题,严禁转载,违者追究法律责任

     

      本次是最后一篇免费的考试题解,以后的考试题目以及题解将会以付费的方式阅读,题目质量可以拿本次作为参考

     

      本来半个月前就已经搞得差不多了,然后给一位神犇orz看(神犇orz都是很忙的!),就一直听取意见修修改改呀拖到了半个月之后,不过这也是为了能够做到完美吧

    T1-apple-1s

      第一题是一道贪心的水题

      

      天数只有两天,不是今天吃就是明天吃,我们将 b[i]=min(x[i],y[i])定为基础开心值,也就是说不论哪天吃都至少可以得到这个分数,于是只用考虑在哪天吃可以得到比基础更高的分数

      那么我们将第一天的开心值减去第二天的开心值,得到一个差值,若为正数,则表示第一天比第二天优,若我们在第一天吃,那么除了得到基础的分数以外,还可以得到第一天多出的额外分数。如 x[1]=5,y[1]=3,不论在哪天吃至少可以得到 3 分,若在第一天吃还可以得到额外的 2 分

      则将 b[i] 这个差值按降序排序,然后从前往后贪心即可

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<cstdio>
     6 #include<cmath>
     7 using namespace std;
     8 
     9 struct data
    10 {
    11     long long n,a,b,x;
    12 }
    13     a[100001];
    14 long long n,m,i,ans;
    15 bool cmp(data x,data y)
    16 {
    17     return x.x>y.x;
    18 }
    19 int main()
    20 {
    21     freopen("apple.in","r",stdin);
    22     freopen("apple.out","w",stdout);
    23     cin>>n>>m;
    24     for (i=1;i<=n;i++)
    25         {
    26             cin>>a[i].n>>a[i].a>>a[i].b;
    27             a[i].x=a[i].a-a[i].b;
    28         }
    29     sort(a+1,a+1+n,cmp);
    30     for (i=1;i<=n;i++)
    31         {
    32             if (m-a[i].n>=0)
    33                 {
    34                     m-=a[i].n;
    35                     ans+=a[i].a*a[i].n;
    36                 }
    37             else
    38                 {
    39                     ans+=a[i].a*m+a[i].b*(a[i].n-m);
    40                     m=0;
    41                 }
    42         }
    43     cout<<ans<<endl;
    44     return 0;
    45 }

      然后我来讲讲60分的做法,你强行不开 long long 就可以得到60分的高分!

     

    T2-mos-1s

      第二题DP,决策比较难想到,但是代码很容易写,原题是BZOJ2072和POI2004的,但BZOJ把题删了,POI根本上不去……坑爹呐,还是要贴上题目

          

      先把时间升序排序,依次标号为1~n

      我们考虑两种决策,第一种先让 1 号和 n 号过桥,然后 1 号返回,此时消耗 a[n]+a[1] 的时间,第二种是让 1 号和 2 号过桥,然后 1 号送回火把,n 号再跟 n-1 号过桥,最后 2 号送回火把,这样消耗 a[n]+2*a[2]+a[1] 的时间。显然第一种方案只能送走一个人,第二种方案可以送走两个人

      这两种方案是最优的,但貌似证明比较难,这也导致了决策比较难想

      设 f[i] 为送走到第 i 个人所用的时间,预处理出 f[n]=a[n]+a[1],因为它不存在第二种两个人一起走的方案

      状态转移方程   f[i]=min(a[i]+a[1]+f[i+1],a[i+1]+2*a[2]+a[1]+f[i+2])   i=n-1~3

      目标状态就是 f[3]+a[2]

      这里有一个点要特判,就是 n=1 或 2,此时答案就是 a[n]

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<cstdio>
     6 #include<cmath>
     7 using namespace std;
     8 
     9 const int N=100004;
    10 int n,a[N],f[N];
    11 int main()
    12 {
    13     freopen("mos.in","r",stdin);
    14     freopen("mos.out","w",stdout);
    15     scanf("%d",&n);
    16     int i;
    17     for (i=1;i<=n;i++) scanf("%d",&a[i]);
    18     sort(a+1,a+1+n);
    19     if (n==1||n==2)
    20     {
    21         printf("%d
    ",a[n]);
    22         return 0;
    23     }
    24     f[n]=a[n]+a[1];
    25     for (i=n-1;i>2;i--)    f[i]=min(a[i]+a[1]+f[i+1],a[i+1]+2*a[2]+a[1]+f[i+2]);
    26     printf("%d
    ",f[3]+a[2]);
    27     return 0;
    28 }

     

    T3-sta-2s

      题目给的是两秒哈,然而极限数据最后被我0.998s跑过了

      这题是BZOJ1131和POI2008的原题,结果跟上一题一样在网上已经失传了

      话说大家都讲这是个树形DP模板题,然而我考场上没有做出来……

      

      设 f[x] 为以 x 为根的所有节点深度和,size[x] 为以 x 为根的树的节点个数+1(也就是后代的个数加上自己),fa[x] 为 x 的父亲节点

      那么转移 f[x]=(f[fa[x]]-size[x])+(n-size[x]),把它按括号拆成两部分看,当根换成x后,对于它的父亲而言 x 节点的深度是少了 1 的,并且对于x的所有后代深度来说深度也都减少了1,即前半部分为原先总的深度 f[fa[x]] 减去因为换根少的深度 size[x]。后半部分也类似,除了x及其后代后的节点 n-size[x],每一个深度都多了1,所以要加上

      size 和 fa 数组用 DFS 预处理出来,我们用 f[1] 作为初始状态,它就是所有深度和,在预处理的时候统计统计,DP 用 BFS 转移

      因为 n 很大,所以 DFS 的时候要手写栈

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<cstdio>
     6 #include<cmath>
     7 #include<stack>
     8 #include<queue>
     9 using namespace std;
    10 
    11 const int N=1000001;
    12 stack<int> s;
    13 queue<int> q;
    14 int next[N*2],first[N],v[N*2],d[N],size[N],fa[N],j[N];
    15 long long f[N],k;
    16 bool g[N];
    17 inline void read(int &re)
    18 {
    19     char ch=getchar();
    20     re=0;
    21     while (ch>='0'&&ch<='9')
    22     {
    23         re=re*10+ch-'0';
    24         ch=getchar();
    25     }
    26 }
    27 int main()
    28 {
    29     freopen("sta.in","r",stdin);
    30     freopen("sta.out","w",stdout);
    31     int i,x,n,m;
    32     read(n);
    33     for (i=1;i<n;i++)
    34     {
    35         read(x);
    36         read(v[i]);
    37         next[i]=first[x];
    38         first[x]=i;
    39         v[i+n-1]=x;
    40         next[i+n-1]=first[v[i]];
    41         first[v[i]]=i+n-1;
    42         size[i]=1;
    43     }
    44     size[n]=1;
    45     s.push(1);
    46     j[1]=first[1];
    47     fa[1]=-1;
    48     while (!s.empty())
    49     {
    50         x=s.top();
    51         for (;j[x];j[x]=next[j[x]])
    52             if (!fa[v[j[x]]])
    53             {
    54                 fa[v[j[x]]]=x;
    55                 k=d[v[j[x]]]=d[x]+1;
    56                 f[1]+=k;
    57                 j[v[j[x]]]=first[v[j[x]]];
    58                 s.push(v[j[x]]);
    59                 break;
    60             }
    61         if (!j[x])
    62         {
    63             s.pop();
    64             size[fa[x]]+=size[x];
    65         }
    66     }
    67     q.push(1);
    68     m=1;
    69     while (!q.empty())
    70     {
    71         x=q.front();
    72         q.pop();
    73         for (i=first[x];i;i=next[i])
    74         {
    75             if (g[v[i]]) continue;
    76             g[v[i]]=1;
    77             q.push(v[i]);
    78             k=n-size[v[i]]*2;
    79             f[v[i]]=f[x]+k;
    80             if (f[v[i]]>f[m]||(f[v[i]]==f[m]&&v[i]<m)) m=v[i];
    81         }
    82     }
    83     printf("%d
    ",m);
    84     return 0;
    85 }

     

    T4-tet-1s

      BZOJ1106与POI2007的原题,BZOJ又把它删掉了T.T

      考试的时候我用了一个奇奇怪怪的方法把它做出来了,虽然不是正解,但是可以A,还跑得挺快

      

      首先我们肯定是每次找最近的两个数字,然后通过交换把它们消去,如 1…2…1…2 这种情况先消去1还是先消去2的结果是一样的

      设原栈为A,新建一个栈为B,每次将A栈顶元素取出丢进B里,如果B内已有相同元素,根据其距离B栈顶的距离更新答案,然后在栈中消去该数字。如果B内没有的话就压进栈,并且记录其位置,这样不断循环直到A为空为止

      证明自己脑补一下(其实可以用“反正”法)

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<cstdio>
     6 #include<cmath>
     7 #include<stack>
     8 using namespace std;
     9 
    10 stack<int> a,b,c;
    11 int n,i,x,ans;
    12 bool f[50001];
    13 int main()
    14 {
    15     freopen("tet.in","r",stdin);freopen("tet.out","w",stdout);
    16     scanf("%d",&n);
    17     for (i=1;i<=n*2;i++) scanf("%d",&x),a.push(x);
    18     while (!a.empty())
    19         {
    20             x=a.top();
    21             a.pop();
    22             if (f[x])
    23                 {
    24                     while (b.top()!=x) c.push(b.top()),b.pop(),ans++;
    25                     b.pop();
    26                     while (!c.empty()) b.push(c.top()),c.pop();
    27                 }
    28             else f[x]=1,b.push(x);
    29         }
    30     printf("%d
    ",ans);
    31     return 0;
    32 }

     

    T5-bags-1s

      RQNOJ 123,http://www.rqnoj.cn/problem/123

      不得不说,RQNOJ的机子跑得好慢呀,5*10的数据范围本地跑 0.2s,服务器上愣是把我卡掉了,最后只好写了一份 Pascal 交上去

      本地跑

      

      OJ上跑

      

      咳咳,言归正传

      普通的背包是求出最优的那一种方案,方程转移是 f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]),相当于把 2 个变量经比较后丢到 1 个变量里,也就是 k=1 时的情况。而现在我们需要求最优的前 k 组方案,那么可以把数组再增加一维,变成把 2k 个变量经比较后丢进 k 个数里,也就是 2 个线性表丢进 1 个线性表里,由于线性表内数据是单调下降的,则可以按照归并排序的做法做

      实现操作中还可以滚掉第一维,那么 j 就要递减枚举

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<cstdio>
     6 #include<cmath>
     7 using namespace std;
     8 
     9 const int V=5001,K=51,maxint=2147483647;
    10 int f[V][K],g[K];
    11 int main()
    12 {
    13     freopen("bags.in","r",stdin);
    14     freopen("bags.out","w",stdout);
    15     int i,j,n,m,s,ans=0,q1,q2,k,w,v;
    16     scanf("%d%d%d",&m,&s,&n);
    17     for (i=0;i<=s;i++)
    18         for (j=1;j<=m;j++) f[i][j]=-maxint;
    19     f[0][1]=0;
    20     for (i=1;i<=n;i++)
    21     {
    22         scanf("%d%d",&w,&v);
    23         for (j=s;j>=w;j--)
    24         {
    25             if (f[j-w][1]<0) continue;
    26             q1=q2=1;
    27             for (k=1;k<=m;k++)
    28                 if (f[j-w][q1]+v>f[j][q2]) g[k]=f[j-w][q1++]+v;
    29                 else g[k]=f[j][q2++];
    30             for (k=1;k<=m;k++) f[j][k]=g[k];
    31         }
    32     }
    33     for (i=1;i<=m;i++) ans+=f[s][i];
    34     printf("%d
    ",ans);
    35     return 0;
    36 }

     

      其实本来还准备了 3 道题的,不过那三道一道是国家集训队的作业题,一道是省队的考试题,最后一道是有版权的原创题(发了的话XXX会Heck我的QuQ)

      那也就不能放进“水题”合集了吧

      另外,T5有数据,自己到 OJ 上测去

     

      联系方式:http://www.cnblogs.com/hadilo/p/5932395.html

  • 相关阅读:
    ios手势复习值之换图片-转场动画(纯代码)
    ios地图小例子和手势的使用 供大家参考一下呦
    basicAnimation移动图形
    一个layer可以跟着画完的线移动ios程序 好玩啊。
    kvo深入浅出举例
    kvc简单实现
    block 浅析
    从相册中取图片
    绘图quartz之渐变
    绘图quartz之加水印
  • 原文地址:https://www.cnblogs.com/hadilo/p/5946976.html
Copyright © 2011-2022 走看看