20190817DP练习

ennn,今天是0x125E591的生日 大家+1s.-xcc

T1:LG奇怪的电梯.

S1:对每一个点建合法的边,跑最短路.(ps:因为不是DP,不上码了.)

T2:LG尼克的任务.

S2:遗憾考试卡在这个题上,最后连暴力都没打.看到给定一段段区间关联交并问题,自然地联想到了昨天做LG上的饥饿的奶牛   ,于是设了f[ i ][ 0/1]表示为第 i 段选或不选并保证前 i 段都会合法覆盖的情况下最少工作时间,f[ i ][ 0 ]则枚举i之前的区间k能覆盖i区间的进行f[ i ][ 0 ]=max{f[k][1]}的转移,f[ i ][ 1 ]则变得不好处理,要去找不覆盖i区间的区间k,并找到最小代价v将Σk覆盖,f[ i ][1]=max(f[ i ][ 1],v+time[i]),最后发现v怎么也找不出来,并发现后面的选择还会影响前面的状态,这样推dp显然是不行的.看了题解,发现果然要倒推,因为后面的状态影响前面的状态.记录此时不是任务开头,就f[ i ]=f[ i+1]+1,有任务就执行,f[ i ]=Σmax(f[ i ],f[ i+len]),建议调试样例,自然会懂.

总结:①平静下来,不会正解打暴力.②MS:code once,think twice.

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
#define e exit(0)
#define R register
int n,k,id=1,sum[10010],f[10010];
struct bian{
    int L,v;
}len[10010];
bool cmp(bian a,bian b){return a.L>b.L;}
int main()
{
    freopen("LIGNJA.in","r",stdin);
    freopen("LIGNJA.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(R int i=1;i<=k;++i){
        scanf("%d%d",&len[i].L,&len[i].v);
        ++sum[len[i].L];
    }
    sort(len+1,len+1+k,cmp);
    for(R int i=n;i>=1;--i){
        if(!sum[i])
            f[i]=f[i+1]+1;
        else{
            for(R int j=1;j<=sum[i];++j){
                f[i]=max(f[i],f[i+len[id].v]);
                ++id;
            }
        }
    }
    printf("%d",f[1]);
}

T5:LGP1233

S5:不知道为什么考试脑抽,对于双要求的最长不下降子序列,自己就将两个要求一起判断求???其实要联想到LG友好城市.将一个要求sort确定下来,另一个在此基础上求.

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
#define e exit(0)
#define R register
int n,q[5010],lenth;
struct bian{
    int L,W;
}len[5010];
bool cmp(bian a,bian b){
    if(a.L==b.L)
        return a.W<=b.W;
    else return a.L<b.L;
}
int find(int x)
{
    int l=1,r=lenth;
    while(l<r){
        int mid=(l+r)>>1;
        if(q[mid]<=x) r=mid;
        else l=mid+1;
    }
    return l;
}
int main()
{
    freopen("STICK.in","r",stdin);
    freopen("STICK.out","w",stdout);
    scanf("%d",&n);
    for(R int i=1;i<=n;++i)
        scanf("%d%d",&len[i].L,&len[i].W);
    sort(len+1,len+1+n,cmp);
    q[++lenth]=len[1].W;
    for(R int i=2;i<=n;++i)
    {
        if(len[i].W<q[lenth])
            q[++lenth]=len[i].W;
        else if(len[i].W>=q[lenth]){
            int id=find(len[i].W);
            q[id]=len[i].W;
        }
    }
    printf("%d",lenth);
    return 0;
}

PS:①:当一个元素一个标志相同时,另一个标志小sort时放前,这样单调栈求最长下降子序列时长度会往小的方面发展.

   ②:二分查找找维护单调下降的序列中第一个比它小的是这样写,其他要求跟题目意思来,注意等号给左边给右边关系"比它小比它大问题".

T4:LGP1220

S4:太菜了,看题解才懂.设f[ L ][ R ][ 0/1 ]表示为走完区间[ L , R ]的耗电量,并且结束时人在L / R点.在转移方程中我们要体现一直走或折返走这个过程.思路转移在题解中写的很清楚了.但有几个细节蒟蒻认为比较重要,想了一下才明白.

① f[ L ][ R ][ 0 ]会由f[ L+1 ][ R ][ 1 ]转移过来,顺推,L由L+1转移,看似违背了后效性,其实不然,我们会发现len从小到大枚举,f[ L+1 ][ R ]会提前更新.

② 对于任意的f[ L ][ R ][ 0 ]不一定都有意义(可对样例的各个区间输出一下).因为开始位置决定了有些区间无法走的,这就证明了为什么初始化是有意义的.

 

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define re register
long long n,c,pos[1010],sum[1010],w[1010],f[1010][1010][2];
int main()
{
    freopen("power.in","r",stdin);
    freopen("power.out","w",stdout);
    scanf("%lld%lld",&n,&c);
    for(re int i=1;i<=n;++i){
        scanf("%lld%lld",&pos[i],&w[i]);
        sum[i]=sum[i-1]+w[i];
    }
    memset(f,0x7f,sizeof(f));
    f[c][c][0]=f[c][c][1]=0;
    for(re int len=2;len<=n;++len)
        for(re int L=1;L+len-1<=n;++L){
            int R=L+len-1;
            f[L][R][0]=min(f[L+1][R][0]+(pos[L+1]-pos[L])*(sum[L]+sum[n]-sum[R]),f[L+1][R][1]+(pos[R]-pos[L])*(sum[L]+sum[n]-sum[R]));
            f[L][R][1]=min(f[L][R-1][1]+(pos[R]-pos[R-1])*(sum[L-1]+sum[n]-sum[R-1]),f[L][R-1][0]+(pos[R]-pos[L])*(sum[L-1]+sum[n]-sum[R-1]));
        }
    printf("%lld",min(f[1][n][0],f[1][n][1]));
    return 0;
}

 T3:对于一个多边形来说,在该多边形内任取两点,如果这两点连成的线段落在多边形内,则称这样的多边形为凸多边形。平面上有 N 个坐标值为自然数的圆点。顶点数最多凸多边形是指由给定的圆点中的一部分组成的凸多边形,它包含最大可能的顶点数。原点,即坐标内中心(0,0)必须是顶点数最多凸多边形的一个顶点。编写程序求出这样的凸多边形的最大顶点数。注意一个多边形的连续的边不能是平行的.(好像OJ没这个题)

S3:依旧太菜了,看题解才懂.先对坐标进行极角排序(为了保证DP的无后效性).设 f[ i ][ j ]为 i为倒数第二个点,j为倒数第一个点构成的凸多边形有最多点,对当前f[ i ][ j ],我们枚举点k,如果k , i  , j能构成凸多边形,我们进行f[ i ][ j ]=max{f[ k ][ i ]+1},即在f[ i ][ k ]构成的凸多边形加入 j点,这时f[ k ][ i ]-->f[ i][ j ].

细节:①我们将'0' , ' n+1 '其实视为一个点(原点).所以 j 要枚举到 n+1,其实为了最后ans+1(加上原点),k从0开始枚举,是为了f[ 0 ][ i ]=1的初始化起作用.

   ②三点是否在同一凸多形,本质是用叉乘判断.对于( a,b),(c,d),(e,f),形成两个向量①( a - c, b- d),②(c - e,d-f).①x②>0即在同一凸多边形.化简为a(d-f)+c(f-b)+e(b-d)>0

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
#define R register
int n,ans,f[110][110];
struct dian{
    int x,y;
    bool operator<(const dian &a) const{
        return 1.0*y/x<1.0*a.y/a.x;
    }
}dot[110];
bool check(int x,int y,int z)
{
    int a=dot[x].x,b=dot[x].y;
    int c=dot[y].x,d=dot[y].y;
    int e=dot[z].x,f=dot[z].y;
    return (a*(d-f)+c*(f-b)+e*(b-d) > 0);
}
int main()
{
    freopen("POLYGON.in","r",stdin);
    freopen("POLYGON.out","w",stdout);
    scanf("%d",&n);
    for(R int i=1;i<=n;++i)
        scanf("%d%d",&dot[i].x,&dot[i].y);
    sort(dot+1,dot+1+n);
    for(R int i=1;i<=n;++i)
        f[0][i]=1;
    for(R int i=1;i<=n;++i)
        for(R int j=1;j<=n+1;++j)
            for(R int k=0;k<i;++k)
                if(check(i,j,k))
                    f[i][j]=max(f[i][j],f[k][i]+1);
    for(R int i=1;i<=n;++i)
        ans=max(ans,f[i][n+1]);
    printf("%d",ans);
    return 0;
}

 

 

posted @ 2019-08-17 20:03  xqyxqy  阅读(220)  评论(0编辑  收藏  举报