2018.4.18

写在前面

今天晚上我平淡的生活中突然来了点刺激。。。
一个晚上打的一场dp练习赛。。
由于缺乏大局观,我做了一个晚上的T1,最后才发现难度并不是递增的!!!而T1也没处理好。。。
被虐的生活不能自理。。
我很荣幸地当了rank 2!!!(当然是倒数的)差点爆0了。。。
这里写图片描述
该好好反思一下自己的心不在焉了。。

T1

题面

1、偷懒的马拉松
【问题描述】
奶牛们浑身难受,有感于此,FJ让他的奶牛们参与了各种各样的体育锻炼活动;
他珍爱的贝茜参加了一个跑步培训班,她最终预计要跑一个马拉松,从FJ农场附近的城市中心到FJ的农场;
这场马拉松包含N个逐一通过的检查点,检查点1是起点,检查点N是终点。
贝茜被要求一个接一个的通过所有检查点,但是作为一只懒惰的母牛,她有k次机会去跳过某个检查点来缩短她马拉松的路程。她不能跳过检查点1或检查点N,理所应当的,这种放肆显然会被发现的;
请帮助贝茜找到一个最小的距离,在她跳过某个检查点后。
注意比赛设置的检查点是在城市的一个个网格中,设第一个检查点坐标为(x1,y1),第二个为(x2,y2),它们之间的距离是通过|x1-x2| + |y1-y2|这个公式来计算的。这种测量距离的方法—通过x之间的差的绝对值加上y之间差的绝对值,有时被称为曼哈顿距离,因为它反映出了一个在市中心网格的事实,你可以平行于x轴抑或y轴走,但是你绝对不能斜着走一条直线。
(良心总结:在二维平面上有N个点,从(x1,y1)到(x2,y2)的代价为|x1-x2|+|y1-y2|。
求从1号点出发,按从1到N的顺序依次到达每个点的最小总代价。
你有K次机会可以跳过某个点,不允许跳过1号点或N号点。)

【输入】
第一行给你一个N的值和k次跳过的机会
接下来的N行,每行含有两个以空格隔开的整数,x和y代表一个检查点((-1000 <= x <= 1000, -1000 <= y <= 1000) 。给出的检查点是必须去通过的。
注意,路程中可能会跨越某个点几次,几个检查点可能会出现在同一个实际的位置上。
在这种检查点上,贝西只能跳过一个检查点,而非这个位置上的全部检查点。

【输出】
输出贝茜在跳过某个检查点后跑的最短路程。不要忘记在输出结束的时候输出一个换行。
在这个示例中,跳过检查点(8,3)和(10.-5)可以得到最短的距离4.

【输入样例1】
5 2

0 0

8 3

1 1

10 -5

2 2

【输出样例1】
4

【数据范围】
3<=k小于n<=500

解析

dp[i][j]表示前i个过路点跳过j个的最短路径。
资源分配问题。
枚举i,每个过路点。
枚举j,跳过j个。
枚举L,从i前l个点(i-l到i-1)设为跳过。
dp[i][j]=max(dp[i][j],dp[i-l-1][j-l]+dis[i-l-1][i]);
我的资源分配还是有点薄弱的。

代码

#include<bits/stdc++.h>
using namespace std;
int dp[510][510]={},x[510]={},y[510]={},dis[510][510]={};
int n,k;
void init()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i)
      scanf("%d%d",&x[i],&y[i]);
    for(int i=1;i<=n;++i)
      for(int j=i+1;j<=n;++j)
        dis[i][j]=abs(x[i]-x[j])+abs(y[j]-y[i]);
}
void work()
{
    memset(dp,10,sizeof(dp));
    dp[1][0]=0;
    for(int i=2;i<=n;++i)
      for(int j=0;j<=k&&j<=i-2;++j)
        for(int l=0;l<=j;++l)
          dp[i][j]=min(dp[i][j],dp[i-l-1][j-l]+dis[i-l-1][i]);
}
int main()
{
    init();
    work();
    printf("%d\n",dp[n][k]);
    return 0;
}

T2

题面

2、养猪
Description
你有一个猪圈,有N头猪,每天你最多可以杀一头猪卖钱,收益就是猪的体重。但是每过一天猪的体重都会下降Pi,问K天内你的最大获利。
Input
第一行两个正整数N、K;
第二行N个数表示猪的初始重量A[i];
第三行N个数表示P[i];
Output
一行一个整数表示最大的获利。
Sample Input
2 2 10 10 1 2
Sample Output
19
Hint
20% 数据满足 1≤N≤20
100%数据满足1≤N≤1000, 体重≤10^5。

解析

dp[i][j]前i头猪,杀j天(杀j头)。
要先对数据进行排序。
为什么排序???
因为相同天数杀相同头牛,不及损耗其总量是一样的,那么就要减去损耗,显然损耗大的放前面更合理。用损耗大的作为损耗小的上一阶段,可以将损耗小的放在损耗大的后面,或者将其覆盖。
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+max(a[i]-p[i]*(j-1),0)).
方程前这是这头不杀,而后者是杀这头。。
那么可以将前一维优化掉。

代码

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int a,p;
}a[1010];
int n,k;
int dp[1010]={};
bool mys(node a,node b)
{
    return a.p>b.p;
}
void init()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i)
      scanf("%d",&a[i].a);
    for(int i=1;i<=n;++i)
      scanf("%d",&a[i].p);
    sort(a+1,a+n+1,mys);
}
void work()
{
    for(int i=1;i<=n;++i)
      for(int j=k;j>=1;--j)
        dp[j]=max(dp[j],dp[j-1]+max(a[i].a-a[i].p*(j-1),0));
}
int main()
{
    init();
    work();
    int maxx=0;
    for(int j=1;j<=k;++j)
      if (dp[j]>maxx) maxx=dp[j];
    cout<<maxx;
    return 0;
}

T3

题面

3、贝茜的派
Description
Bessie has broken into Farmer John’s house again! She has discovered a pile of lemons and a pile of oranges in the kitchen (effectively an unlimited number of each), and she is determined to eat as much as possible.

Bessie has a maximum fullness of T (1≤T≤5,000,000). Eating an orange increases her fullness by A, and eating a lemon increases her fullness by B (1≤A,B≤T). Additionally, if she wants, Bessie can drink water at most one time, which will instantly decrease her fullness by half (and will round down).

Help Bessie determine the maximum fullness she can achieve!
奶牛Bessie潜入了农夫约翰的家,她发现这里有无穷无尽的柠檬派和橘子派。
Bessie的饱胀值一开始是0,且上限是T,每个柠檬派可以提供A点饱胀值,每个橘子派可以提供B点饱胀值。
Bessie可以不断地吃东西,如果她的饱胀值没有超出T的话。同时,Bessie有一次喝水的机会,喝完后,她的饱胀值将减少一半(往下取整)。
请计算出Bessie的饱胀值最多可以达到多少。
Input
The first (and only) line has three integers T, A, and B.
Output
A single integer, representing the maximum fullness Bessie can achieve.
Sample Input
8 5 6
Sample Output
8

解析

背包。
用两个pie做完全背包。
将背包能达到的饱胀值/2再存一个背包。用这个背包做喝水之后能达到的饱胀值。
通过这个/2后的背包再做完全背包。通过这个背包所能达到的饱胀值,得出最终答案。

代码

#include<bits/stdc++.h>
using namespace std;
int t,a,b;
bool dp[5000010]={},dp2[5000010]={};
int main()
{
    scanf("%d%d%d",&t,&a,&b);
    dp[0]=1;
    for(int i=0;i<=t;++i)
      if (dp[i]) 
        {
          if (i+a<=t)
            dp[i+a]=1;
          if (i+b<=t)
            dp[i+b]=1;
          dp2[i/2]=1;
        }
    for(int i=0;i<=t;++i)
      if (dp2[i])
        {
          if (i+a<=t)
            dp2[i+a]=1;
          if (i+b<=t)
            dp2[i+b]=1;
        }
    for(int i=t;i>=0;--i)
      if (dp2[i]) 
        {
          printf("%d",i);
          break;
        }
    return 0;
}

T4

题面

背包问题

问题描述
从T组物品中选出一些物品,放入背包中,求剩余空间的最小值。
限制条件:从每组物品中挑选物品必须要选取连续的一段。就是说,如果这组物品共有n个: 物品1、物品2、物品3、…、物品n,那么只能选取物品i、物品i+1、…、物品j,其中1<=i<=j<=n,或者不选。
输入
第一行为两个用空格隔开的正整数v和T。表示背包的空间和物品的组数。接下来有T行,每行先是一个正整数ni,表示这组物品有ni个,然后ni个正整数,表示每个物品的大小。
输出
仅一个数,表示剩余空间的最小值。
样例输入
100 3
3 7 6 8
2 80 70
4 101 108 103 150
样例输出
6

【样例说明】 第1组选6、8,第2组选80,第3组不选。

【限制】
60%的数据满足:1 <= ni <= 10
100%的数据满足:1 <= ni <= 100,1<=v<=5000,1<=T<=10

解析

分组背包。
dp[i][j]表示i组是否能装到体积为j。
每次枚举起点到终点,每次都枚举肯定会炸的。
所以要存一个前缀和,方便每次o(1)求出区间和。
而且要考虑i组不取的情况。

代码

#include<bits/stdc++.h>
using namespace std;
int n[11]={},sum[11][110]={};
bool dp[11][5010]={};
int main()
{
    int v,t,x;
    scanf("%d%d",&v,&t);
    for(int i=1;i<=t;++i)
      {
        scanf("%d",&n[i]);
        for(int j=1;j<=n[i];++j)
          scanf("%d",&x),sum[i][j]=sum[i][j-1]+x;//每组求个前缀和。 
      }
    dp[0][0]=1;
    for(int i=1;i<=t;++i)
      {
        for(int st=1;st<=n[i];++st)
          for(int en=st;en<=n[i];++en)//枚举该组起点物品到终点物品。 
            {
              int vv=sum[i][en]-sum[i][st-1];
              for(int j=v;j>=vv;--j)
                dp[i][j]=dp[i-1][j-vv]||dp[i][j];
            }
        for(int j=v;j>=0;--j)
          dp[i][j]=dp[i-1][j]||dp[i][j];//i组不取也要存储。 
      }
    for(int i=v;i>=0;--i)
      if (dp[t][i]) 
        {
          printf("%d",v-i);
          break;
        }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值