码迷,mamicode.com
首页 > 其他好文 > 详细

DP练习~今天s酱不在家~

时间:2017-09-12 09:55:59      阅读:203      评论:0      收藏:0      [点我收藏+]

标签:namespace   blog   水题   陷阱   区间   scan   数组   clu   语文   

RT,今天s酱不在家~

于是w就可以为所欲为的刷水题啦~

因为w的DP学的实在是……,于是今天w去刷了几道DP题练手>_<

 


 

 

1.能量项链(luogu P1063)

这题面……考验语文能力的时刻到了x

一道区间DP

这个环结构看起来就很麻烦的样子……

那就拆了O(∩_∩)O~

每次读入一个a[i]的时候,顺手把a[n+i]也赋成这个值

这样处理完后就是一个长度为原环两倍的链

在这个链上能找到所有长度为n+1的可能排列

//长度n+1是因为最后一个珠子的尾标是第一个的头标,在链中即下一个的头标

接下来就DP吧

f[i][j]表示区间长度为i,区间左端点的头标记是j

有了这两个当然就能算出右端点的尾标记

然后枚举一波断点(区间DP的套路

因为每个珠子有两个标记,又只存了一个数

导致各种+1-1的小细节

qwq看了好久才明白

最后再统计一波答案

 

技术分享
#include<iostream>
#include<algorithm>
using namespace std;
const int N=202;
int n,a[N],ans;
int f[N][N];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
      scanf("%d",&a[i]);
      a[i+n]=a[i];
    }
    for(int i=2;i<=n+1;++i)//区间长度 
    {
      for(int l=1;l+i-1<=2*n;++l)//头标记 
      {
        int r=l+i-1;//尾标记 
        for(int k=l+1;k<=r-1;++k)//中间断点的标记 
        f[l][r]=max(f[l][r],f[l][k]+f[k][r]+a[l]*a[k]*a[r]);
      } 
    }
    for(int i=1;i<=n;++i)//区间开始位置 
    ans=max(ans,f[i][i+n]);
    cout<<ans<<endl; 
    return 0;
}
energy

 

 

 


2.垃圾陷阱(luogu P1156)
 
 

为什么是卡门不是Bessie(

刚一看题面各种mengbier,连DP存什么都想不出来

跑去看题解,类背包?++mengbier

看了好久才看懂……做法很神奇x

学了一种刷表的做法

(乱入的科普:刷表指用现在的状态去更新之后的状态,与之相反的是填表,指用之前的状态更新现在的状态)

DP数组?不存在的 这里的所谓DP数组其实是一个bool数组

里面存的,既不是高度,也不是能量

f[i][j]表示的是在高度为i,时间为j的情况下能否存活

如果能存活的话就向下转移

两个转移方向:堆起来(i+高度)或者吃掉(j+时间)

转移完发现高度超出井深了,因为经过了排序,所以得出的一定是最早的时间,输出就好了

一套循环下去这牛还没有出去,说明它已经在井里gg了

时间从后向前倒,看它活在这世上的最后一秒是什么时候

 

技术分享
//传说中的刷表法qwq 
#include<cstdio>
#include<iostream>
#include<algorithm> 
using namespace std;
int d,g,s=10;
bool f[400][6000];//f[i][j]->高度为i的情况下能存活j的时间 
struct rub{
       int t,h,f;
}r[105];
bool cmp(rub x,rub y)
{
     return x.t<y.t;
}
int main()
{
    cin>>d>>g;
    for(int i=1;i<=g;++i){
      scanf("%d%d%d",&r[i].t,&r[i].f,&r[i].h);
      s+=r[i].f; //计算可能存活的最大时间 
    }
    sort(r+1,r+g+1,cmp);
    f[0][10]=1;
    for(int i=1;i<=g;++i)//垃圾 
    for(int j=d-1;j>=0;--j)//高度 
    for(int k=s;k>=r[i].t;--k)//时间 
    {
      if(!f[j][k])continue;
      if(j+r[i].h>=d)
      {
        printf("%d",r[i].t);
        system("pause");
        return 0;
      }
      else 
      {
        f[j+r[i].h][k]=1;
        f[j][k+r[i].f]=1;
      }
    }
    for(int i=s;i>=1;--i)
    if(f[0][i])
    {
      printf("%d",i);
      system("pause");
      return 0;
    } 
    return 0;      
}
rubbish well

 

 


 3.过河(luogu P1052)

一开始一看,这不是某只⑨的一道题吗?

然后一看数据范围,L<=10^9,1 <= M <= 100

(步长10桥长10^9,心疼这只青蛙一下x)

正常的DP方程很好写

f[j]=min(f[j],f[i]+stn[j])

然而10^9的数据,时间空间都会炸

但是因为长长的桥上只有很少的石子

所以在两个石子之间青蛙君会跳过一段很长的没有石子的距离

就可以把这段距离压缩一下

把距离>100(10*10)的两颗石子中间的距离压到100

……据说还可以压到90?并不理解qwq,反正有100就够用了

 

技术分享
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=50000;
int l,s,t,m,a[200],stn[N],f[N],tmp;
int main()
{
    scanf("%d%d%d%d",&l,&s,&t,&m);
    for(int i=1;i<=m;++i)
    scanf("%d",&a[i]);
    sort(a+1,a+m+1);
    if(s==t)
    {
      int ans=0;
      for(int i=1;i<=m;++i)
      if(!(a[i]%s))++ans;
      printf("%d",ans);
      return 0;
    }
    for(int i=1;i<=m;++i)
    {
      int x=a[i]-a[i-1];
      if(x>100)tmp+=100;
      else tmp+=x;
      ++stn[tmp];
      if(i==m)l=tmp+1;
    }
    int ans=200;
    for(int i=1;i<=l+15;++i)f[i]=200;
    f[0]=0;
    for(int i=0;i<=l+15;++i)
    {
      for(int j=i+s;j<=i+t;++j)
      f[j]=min(f[j],f[i]+stn[j]);
    }
    for(int i=l;i<=l+15;++i)ans=min(ans,f[i]);
    cout<<ans; 
    return 0;
} 
过河

 


呀s酱回来了>_<保存保存……

 

 

DP练习~今天s酱不在家~

标签:namespace   blog   水题   陷阱   区间   scan   数组   clu   语文   

原文地址:http://www.cnblogs.com/ck666/p/7500588.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!