zoukankan      html  css  js  c++  java
  • 【HAOI2011】problem a

    我的思维能力真的上来了好感动555

    原题:

     

    随便点的一道DP题,本来这题以前无数次想不出来,题解好像也看不懂,想的时候都打算放弃了

    但是想着一定要死磕思维能力,最后居然还真的自己做出来了

    果真是以前放弃得太轻易hhh

    首先要确定一下题意,“有ai个人”我感觉也可以理解为>=ai个人

    但是其实后来想一下,如果这样说得话就没必要强调可能有相同分数了

    因为如果是>=ai的话,两个同分的和两个相邻分数的对于合法性其实都一样

    所以认为题意为恰好有ai个人(当然考场上还是大力问监考)

    那么研究一下这个约束条件的限制,可以发现实际上是ai和bi中间摞了n-ai-bi个人

    那么用xi=ai表示这一摞人前面有多少人,yi=n-ai-bi表示摞了多少人

    可以发现对于xi相等而yi不等的两个人一定无法满足

    而xi和yi都相等的人一定一起满足(除非xi和yi相等的人数超过了yi,那么多出者不满足)

    所以可以把xi和yi相等的人合并,令zi表示满足这些人的要去需要再拿多少个假人充数

    (假人就是条件不被满足的人)

    然后灵稽一动,不妨就按xi递增排序,然后考虑枚举到的每个人是否满足要求

    如果这样做的话状态可以设为f[i]表示把前i个人安排上需要的最少说谎人数

    递推求解

    这样好像不太对,有后效性

    即假设现在要那一个假人充数,那这个假人是前面考虑过的,还是后面没考虑的,还是把已经满足的撤了

    你不知道,会影响决策

    我的思路转了一圈之后,找不到什么有可能的做法,再回来研究这样做,发现各种细节美好得不正常

    于是决定随便写写试试

    然后就ac了

    我尻 这都行

    现在再来考虑算法得正确性,可以发现刚才得顾虑是多余的

    因为所有没满足要求的假人都是一样的,而易证假人是充足的,因为如果假人不够则与总人数冲突

    所以我们完全不用考虑假人从哪来,只需要直到前边已经有i个人被安排了就行

    最后整理一下思路

    首先令xi=ai,yi=n-ai-bi,zi=需要补的假人

    然后按xi递增排序,走递推

    f[i]初值为i,包括f[0]=0

    递推公式为f[a[i].x+a[i].y]=min(f[a[i].x+a[i].y],f[a[i].x]+a[i].z)

    需要注意a[i].x和a[i-1].x中间可能隔了几个数,而这些状态实际受f[a[i-1].x]的影响

    因为中间可以直接丢一个假人过去把之前的更优决策继承过来

    也就是说f[a[i-1].x+j]应该等于f[a[i-1].x]+j,f[a[i].x]应该等于min(f[a[i].x],f[a[i-1].x]+a[i].x-a[i-1].x)

    我的写法是用一个temp跟着a[i].x走,每次让temp++就令f[temp]=min(f[temp],f[temp-1]+1)

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 using namespace std;
     5 struct nds{int x,y,z;}a[110000],b[110000];  int m=0;
     6 int n;
     7 int f[110000];
     8 bool cmp(nds x,nds y){  return x.x==y.x ? x.y<y.y : x.x<y.x;}
     9 int main(){
    10     freopen("ddd.in","r",stdin);
    11     cin>>n;
    12     int l,r;
    13     for(int i=1;i<=n;++i){
    14         scanf("%d%d",&l,&r);
    15         a[i].x=l,a[i].y=n-l-r;
    16     }
    17     sort(a+1,a+n+1,cmp);
    18     for(int i=1;i<=n;++i)if(a[i].y>0){
    19         if(a[i].x!=b[m].x || a[i].y!=b[m].y){
    20             b[++m]=a[i];
    21             b[m].z=a[i].y-1;
    22         }
    23         else  b[m].z=(b[m].z ? b[m].z-1 : 0);
    24     }
    25     for(int i=1;i<=n;++i)  f[i]=i;
    26     f[0]=0;
    27     int tmp=0;
    28     for(int i=1;i<=m;++i){
    29         while(tmp<b[i].x){
    30             ++tmp;
    31             f[tmp]=min(f[tmp],f[tmp-1]+1);
    32         }
    33         f[b[i].x+b[i].y]=min(f[b[i].x+b[i].y],f[b[i].x]+b[i].z);
    34     }
    35     while(tmp<n){
    36         ++tmp;
    37         f[tmp]=min(f[tmp],f[tmp-1]+1);
    38     }
    39     printf("%d
    ",f[n]);
    40     return 0;
    41 }
    View Code
  • 相关阅读:
    3. 算法分析
    4. union-find算法
    1.基础编程模型和数据抽象
    2. 背包,队列和栈
    .NET Core 单元测试
    ASP.NET Core Web API
    ASP.NET Core MVC 之区域(Area)
    ASP.NET Core MVC 之依赖注入 Controller
    ASP.NET Core MVC 之依赖注入 View
    微服务架构学习与思考(04):微服务技术体系
  • 原文地址:https://www.cnblogs.com/cdcq/p/12024283.html
Copyright © 2011-2022 走看看