• dp杂题(根据个人进度选更)


    ----19.7.30 今天又开了一个新专题,dp杂题,我依旧按照之前一样,这一个专题更在一起,根据个人进度选更题目;

    dp就是动态规划,本人认为,动态规划的核心就是dp状态的设立以及dp转移方程的推导,这也是训练的重中之重,所以代码不那么重要,重要的就是dp的思想;

    T1:

    A. 消失之物

    题目描述

    ftiasch 有 N 个物品, 体积分别是 W1, W2, ..., WN。 由于她的疏忽, 第 i 个物品丢失了。 “要使用剩下的 N - 1 物品装满容积为 x 的背包,有几种方法呢?” -- 这是经典的问题了。她把答案记为 Count(i, x) ,想要得到所有1 <= i <= N, 1 <= x <= M的 Count(i, x) 表格。

    这道题乍一看以为就是一个裸的背包,但是(本人太弱了)我以上来先想到的是裸的$ 0/1 $背包然后就想跑$ n $遍$  0/1 $背包,但是显然复杂度会爆炸,所以要考虑别的方法,但是还是逃不掉的dp,重点就在状态的转移,这个转移其实可以在之前的0/1背包的基础上进行转移,那么我们可以设f[maxn][2],也就是开一维半的数组,设状态为$ f[j][1] $表示背包容量为j时的方案数,因为题目恶心了我们就是要输出一个矩阵,那么我们就需要再循环i表示当我们去掉i时$  f[j][1] $就是背包容量为j时的方案数,那么就可以列出状态转移方程:(这里的0/1表示能否可以解决!)

    $  f[j][0]+=f[j-v[i]] $

    $  f[j][1]=f[j][0]+f[j-v[i]][1] (j-v[i]>0)$

    $  f[j][1]=f[j][0]  (j-v[i]<=0) $

    然后就结束了,一定要记得多多取模(他让输出最低的一位,所以不那么恶心!),这道题没什么细节,就不站代码了,评论区留给你们!

     UPD:这道题skyh的打法刷新了我的dp观,我是真的震惊

    天黄的这道题使用分治带dp打的,很新颖,不愧是dalao(orz),那么我也稍说一下skyh的思路:

    $ (after 10 mins...) $其实和我的思路差不多,就是分治了一下,qwq

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 const int N=2010;
     5 int n,m,w[N];
     6 short dp[15][N];
     7 void solve(int dep,int l,int r){
     8     if(l==r){
     9         for(int i=1;i<=m;++i) printf("%d",dp[dep-1][i]);
    10         puts("");
    11         return ;
    12     }
    13     int mid=l+r>>1;
    14     for(int i=0;i<=m;++i) dp[dep][i]=dp[dep-1][i];
    15     for(int i=mid+1;i<=r;++i) for(int j=m;j>=w[i];--j) (dp[dep][j]+=dp[dep][j-w[i]])%=10;
    16     solve(dep+1,l,mid);
    17     for(int i=0;i<=m;++i) dp[dep][i]=dp[dep-1][i];
    18     for(int i=l;i<=mid;++i) for(int j=m;j>=w[i];--j) (dp[dep][j]+=dp[dep][j-w[i]])%=10;
    19     solve(dep+1,mid+1,r);
    20 }
    21 int main()
    22 {
    23     scanf("%d%d",&n,&m);
    24     for(int i=1;i<=n;++i) scanf("%d",&w[i]);
    25     dp[0][0]=1; solve(1,1,n);
    26     return 0;
    27 }
    skyh

    //copyright by skyh

    //copy from skyh  orz

    B. 方伯伯的玉米田

    内存限制:128 MiB 时间限制:6000 ms 标准输入输出
     
     

    题目描述

    方伯伯在自己的农田边散步,他突然发现田里的一排玉米非常的不美。
    这排玉米一共有N株,它们的高度参差不齐。
    方伯伯认为单调不下降序列很美,所以他决定先把一些玉米拔高,再把破坏美感的玉米拔除掉,使得剩下的玉米的高度构成一个单调不下降序列。
    方伯伯可以选择一个区间,把这个区间的玉米全部拔高1单位高度,他可以进行最多K次这样的操作。拔玉米则可以随意选择一个集合的玉米拔掉。
    问能最多剩多少株玉米,来构成一排美丽的玉米。

    输入格式

    第1行包含2个整数n,K,分别表示这排玉米的数目以及最多可进行多少次操作。
    第2行包含n个整数,第i个数表示这排玉米,从左到右第i株玉米的高度ai。

    输出格式

    输出1个整数,最多剩下的玉米数。

    这是学长讲过的一道例题,是数据结构优化dp,而且这道题需要证明一个引理;

    引理:所有操作的右端点一定是n(最右侧)的那个点。

    证明:如果将一个区间内的权值都加上一个数,只会出现两种情况:

      1.区间的左侧:

        之前比区间内的数小的在操作之后还是比他小;

        之前比区间内的数大的在操作之后没有他大(比他小!);

        之前比区间内的数大的在操作之后还是比他大;

      所以区间左侧不会降低ans,还可能增加ans

      2.区间的右侧:

        之前比区间内的数小在操作之后还是比他小;

        之前比区间内数大的,现在不一定比他大;

        之前比区间内数大的,现在还是比他大;

     所以区间右侧不会升高ans,还可能降低ans;

    所以要保证答案最优,就要有区间右侧最小,所以就有所有的操作都以n为有区间的端点;

    证明完毕;

    接着回到题解,这里有了上面的引理,我们就能推出dp的状态转移方程;

    设$ f[i][j] $表示以i为结尾,共被拔高了j次的ans,即以i为结尾,共被j个区间覆盖;

    那么,我们根据定义可以推出状态转移方程:

      $ f[i][j]=max{f[k][l]}+1 (1<=k<=i,1<=l<=j ) $且要合法才能转移;

    那么这一看如果暴力求解的话复杂度爆表,所以这个可以使用二维树状数组进行优化,然后就是$ O(n*m) $的复杂度;

    代码实现也很简单:

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cmath>
     4 #include<cstdlib>
     5 #include<cstdio>
     6 using namespace std;
     7 #define re register
     8 int a[10005],c[10005][505],n,m,maxa,ans,sum,res;
     9 int lowbit(int x){return x&(-x);}
    10 void change(int x,int y,int z)
    11 {
    12     int yy=y;
    13     while(x<=maxa+m)
    14     {
    15         y=yy;
    16         while(y<=m+1)
    17         {
    18             c[x][y]=max(c[x][y],z);
    19             y+=lowbit(y);
    20         }
    21         x+=lowbit(x);
    22     }
    23 }
    24 int getsum(int x,int y)
    25 {
    26     int yy=y,sum=0;
    27     while(x>0)
    28     {
    29         y=yy;
    30         while(y>0)
    31         {
    32             sum=max(sum,c[x][y]);
    33             y-=lowbit(y);
    34         }
    35         x-=lowbit(x);
    36     }
    37     return sum;
    38 }
    39 int main()
    40 {
    41     //freopen("simple.txt","r",stdin);
    42     scanf("%d%d",&n,&m);
    43     for(int i=1;i<=n;i++)
    44     {
    45         scanf("%d",&a[i]);
    46         maxa=max(maxa,a[i]);
    47     }
    48     for(int i=1;i<=n;i++)
    49     {
    50         for(int j=m;j>=0;j--)
    51         {
    52             res=getsum(a[i]+j,j+1)+1;
    53             change(a[i]+j,j+1,res);
    54             ans=max(ans,res);
    55         }
    56     }
    57     printf("%d
    ",ans);
    58     return 0;
    59 }
    玉米田

    C. 拦截导弹

    内存限制:512 MiB 时间限制:1500 ms 标准输入输出
     

    题目描述

    某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度、并且能够拦截任意速度的导弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度也不能大于前一发。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

    在不能拦截所有的导弹的情况下,我们当然要选择使国家损失最小、也就是拦截导弹的数量最多的方案。但是拦截导弹数量的最多的方案有可能有多个,如果有多个最优方案,那么我们会随机选取一个作为最终的拦截导弹行动蓝图。

    我方间谍已经获取了所有敌军导弹的高度和速度,你的任务是计算出在执行上述决策时,每枚导弹被拦截掉的概率。

    输入格式

    第一行包含一个正整数

    ,表示敌军导弹数量;

    下面 行按顺序给出了敌军所有导弹信息:

    第i+1行包含2个正整数

    ,分别表示第 枚导弹的高度和速度。

    输出格式

    输出包含两行。

    第一行为一个正整数,表示最多能拦截掉的导弹数量;

    第二行包含n个0到1之间的实数,第i个数字表示第i枚导弹被拦截掉的概率(你可以保留任意多位有效数字)。

    样例

    样例输入

    4
    3 30
    4 40
    6 60
    3 30
    

    样例输出

    2
    0.33333 0.33333 0.33333 1.00000
    

    数据范围与提示

    对于100%的数据,

    这个spj其实是假的,并没有部分分,但是这道题确实是一道好题,其实之前做过拦截导弹的简单版,就是线性dp,所以这还是用线性dp,显然是不行的,因为我以为是spj已经WA0好几次了,因为这道题有三个限制条件,要同时满足三个限制条件,不就是cdq分治吗,然后就把统计答案的dp扔到cdq的过程中,然后,就切了。就这样切了,当然不是,反正我不是1A的,记得开double!

      1 #include<algorithm>
      2 #include<iostream>
      3 #include<cstring>
      4 #include<cstdio>
      5 #include<cmath>
      6 #include<vector>
      7 using namespace std;
      8 const int maxn=100000;
      9 inline int read()
     10 {
     11     int x=0,f=1;char cc;cc=getchar();
     12     while(cc>'9'||cc<'0'){if(cc=='-')f=-1;cc=getchar();}
     13     while(cc>='0'&&cc<='9'){x=(x<<3)+(x<<1)+(cc^48);cc=getchar();}
     14     return x;
     15 }
     16 struct tree
     17 {
     18     int f;double w;
     19     tree(){f=0,w=0;}
     20 }t[maxn];
     21 int n;
     22 int st[maxn],top=0,th,tv;
     23 inline int lowbit(int x){return x&(-x);}
     24 inline void add(int p,int f,double w)
     25 {
     26     while(p<n)
     27     {
     28         if(t[p].f<f)
     29         {
     30             if(t[p].f==0)st[++top]=p;
     31             t[p].f=f;t[p].w=w;
     32         }
     33         else if(t[p].f==f)t[p].w+=w;
     34         p+=lowbit(p);
     35     }
     36     return;
     37 }
     38 tree ask(int p)
     39 {
     40     tree res;
     41     while(p)
     42     {
     43         if(t[p].f>res.f) res=t[p];
     44         else if(t[p].f==res.f) res.w+=t[p].w;
     45         p-=lowbit(p);
     46     }
     47     return res;
     48 }
     49 struct Dan
     50 {
     51     int h,v,f[2],id,t;
     52     double g[2];
     53 }a[maxn],q[maxn];
     54 int wh[maxn],wv[maxn],id[maxn];
     55 int rk[maxn];
     56 inline bool cmp(int x,int y){return a[x].h<a[y].h||(a[x].h==a[y].h&&a[x].id<a[y].id);}
     57 int cmpid(Dan a,Dan b){return a.id<b.id;}
     58 int cnt=0;
     59 void cdq(int l,int r,int mode)
     60 {
     61     if(l==r)
     62     {
     63         if(a[l].f[mode]<1){a[l].f[mode]=1;a[l].g[mode]=1;}
     64         return;
     65     }
     66     int mid=(l+r)>>1;
     67     memcpy(q+l,a+l,sizeof(Dan)*(r-l+1));
     68     int q1=l,q2=mid+1;
     69     for(int i=l;i<=r;i++)
     70         if(q[i].t<=mid)a[q1++]=q[i];     
     71         else a[q2++]=q[i];
     72     cdq(l,mid,mode);
     73     q1=l;
     74     for(int i=mid+1;i<=r;i++)
     75     {
     76         while(q1<=mid && a[q1].id<a[i].id) 
     77             add(a[q1].v,a[q1].f[mode],a[q1].g[mode]),q1++;
     78         tree res=ask(a[i].v);
     79         if(!res.f)continue;
     80         if(res.f+1>a[i].f[mode])
     81         {
     82             a[i].f[mode]=res.f+1;
     83             a[i].g[mode]=res.w;
     84         }
     85         else if(res.f+1==a[i].f[mode]) a[i].g[mode]+=res.w;
     86     }
     87     while(top){t[st[top]].w=0;t[st[top--]].f=0;}
     88     cdq(mid+1,r,mode);
     89     merge(a+l,a+mid+1,a+mid+1,a+r+1,q+l,cmpid);
     90     memcpy(a+l,q+l,sizeof(Dan)*(r-l+1));
     91     return;
     92 }
     93 int main()
     94 {
     95     //freopen("cnm.txt","r",stdin);
     96     n=read();
     97     for(int i=1;i<=n;i++)
     98     {
     99         a[i].h=read();a[i].v=read();a[i].id=i;
    100         wh[i]=a[i].h;wv[i]=a[i].v;
    101         rk[i]=i;
    102     }
    103     sort(wh+1,wh+n+1);
    104     sort(wv+1,wv+n+1);
    105     th=unique(wh+1,wh+n+1)-wh-1;
    106     tv=unique(wv+1,wv+n+1)-wv-1;
    107     for(int i=1;i<=n;i++)
    108     {
    109         a[i].h=th-(lower_bound(wh+1,wh+th+1,a[i].h)-wh)+1;
    110         a[i].v=tv-(lower_bound(wv+1,wv+tv+1,a[i].v)-wv)+1;
    111     }
    112     sort(rk+1,rk+n+1,cmp);
    113     for(int i=1;i<=n;i++)a[rk[i]].t=i;
    114     cdq(1,n,0);
    115     for(int i=1;i<=n;i++)
    116     {
    117         a[i].h=th-a[i].h+1;
    118         a[i].v=tv-a[i].v+1;
    119         a[i].id=n-a[i].id+1;
    120         a[i].t=n-a[i].t+1;
    121     }
    122     reverse(a+1,a+n+1);
    123     cdq(1,n,1);
    124     reverse(a+1,a+n+1);
    125     double smm=0;
    126     int ans=0;
    127     for(register int i=1;i<=n;i++)
    128         ans=max(ans,a[i].f[0]+a[i].f[1]-1);
    129     printf("%d
    ",ans);
    130     for(int i=1;i<=n;i++)
    131         if(a[i].f[0]==ans)
    132             smm+=a[i].g[0]*a[i].g[1]*1ll;
    133     for(int i=1;i<=n;i++)
    134     {
    135         double res=a[i].g[0]*a[i].g[1];
    136         if(a[i].f[0]+a[i].f[1]-1!=ans)printf("%.5lf ",0.0);
    137         else printf("%.5f ",res/smm);
    138     }
    139     return 0;
    140 }
    SDOI拦截导弹

    ///////这个专题的坑还很大,未完待续.........////////

  • 相关阅读:
    获取随机数
    性能测试工具
    Oracle 级联删除
    一些shell用法
    英文
    主题:【元宵赏灯】蛇年杭州元宵赏灯攻略(上城区、滨江区、下城区)
    CListCtrl 列表选中项非焦点时也是藍色
    ASCII码表
    杭州市公积金提取及相关知识
    ListBox设置水平滚动条
  • 原文地址:https://www.cnblogs.com/hzoi-lsc/p/11272454.html
Copyright © 2020-2023  润新知