您的位置:首页 > 编程语言 > C语言/C++

Cpp环境【CQYZOJ1836】【Code[VS]5230】猴子

2016-08-07 16:20 183 查看
【问题描述】


  一只猴子找到了很多香蕉树,这些香蕉树都种在同一直线上,而猴子则在这排香蕉树的第一棵树上。这只猴子当然想吃尽量多的香蕉,但它又不想在地上走,只想从一棵树跳到另一棵树上.同时猴子的体力有限,它不能一次跳得太远或跳得次数太多,每当他跳到一棵树上,就会把那棵树上的香蕉都吃掉。那么,它最多能吃多少个香蕉呢?

【输入格式】


  输入第一行为三个整数,分别是香蕉树的棵数N,猴子每次跳跃的最大距离D,最多跳跃次数M.

  下面N行每行包括两个整数,ai,bi分别表示每棵香蕉树上的香蕉数,以及这棵树到猴子所在树的距离。输入保证这些树按照从近到远排列,并且没有两棵树在同一位置。b0总是为0 .

【输出格式】


  输出只有一行,包含一个整数,为猴子最多能吃到的香蕉数。

【输入样例】


5 5 2

6 0

8 3

4 5

6 7

9 10

【输出样例】


20

【数据范围】


ai<=10000,d<=10000

对于30%的数据,有M<=N<=10,bi<=100

对于50%的数据,有M<=N<=30,bi<=1000

对于100%的数据,有M<=N<=100,bi<=10000

【来源】


三校联考试题

Code[VS]5230 原题传送矩阵

重庆一中题库 原题传送矩阵

【思路梳理】


  DP水题,非常简单,也比较适合给刚刚入门的OIer练手,熟悉一下考场上比较简单的动态规划题目。

  首先考虑直接粗暴的回溯算法,预期得分30分,因为要跳至多m次(至少则是0次),每次可能的方案数至多为n,所以时间复杂度不稳定,最坏为

(并没有那么大):

void solve(int jump_time,int tot,int id)
{//jump_time为当前已经跳过了的次数,tot为总收益,id为当前所处的位置
if(jump_time>m || id>=n)//体力耗尽或者走到终点两种情况都是回溯出口
{
ans=max(ans,tot);//择优
return;
}

for(int i=id+1;i<=n;i++)if(dist[i]-dist[id]<=d)//寻找在编号为id的树的后面的树i使得猴子能够从id跳到i。
solve(jump_time+1,tot+weight[i],i);

ans=max(ans,tot);//需要注意的是,猴子可能在第id棵树已经无法跳到第id+1棵树。
}


考虑动态规划:

当前状态怎么转移过来?从最优的上一次跳跃的终点转移过来。

最优的上一次跳跃的终点怎么找?找能够跳到当前树的所有树中权值和最优的。

当前状态怎么变化?收益要加上这一棵树上的所有果实数。

由此可以写出:

状态函数:f(i,j)=猴子经过i次跳跃到达第j棵树时最多能够吃到的香蕉数

状态转移方程:f(i,j)={max(f(i-1,k)+weight[j] | i-1<=k && dist[j]-dist[k]<=D}

时间复杂度远小于

(因为随着第一重循环次数增加,第二重循环的j循环的范围在缩小,k始终在i-1~j这个区间内)。

【Cpp代码】


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 105
using namespace std;
int n,D,m,weight[maxn],dist[maxn],ans=0;
int d[maxn][maxn];

void dp()
{
//f(i,j)=猴子经过i次跳跃到达第j棵树时最多能够吃到的香蕉数
memset(d,-1,sizeof(d));
//f(i,j)={max(f(i-1,k)+weight[j] | i-1<=k && dist[j]-dist[k]<=D}

d[0][1]=ans=weight[1];//边界:跳跃0次,猴子处于第一棵树

for(int i=1;i<=m;i++)
for(int j=i;j<=n;j++)if(dist[j]<=i*D && dist[j]>=i)
{//每一次必须要往前面跳,意味着如果猴子能跳到下棵树就至少要往前跳1棵树;总共跳了i次,起点是1,那么至少要在i+1棵树的位置;
//同样基于上面的原理,那么最少dist[j]要大于等于i(每一次往前跳1),且小于等于i*D(每一次都往前跳到最远)
int t=-1;
for(int k=j-1;k>=i;k--)if(dist[j]-dist[k]<=D && d[i-1][k]!=-1)
{//d[i-1][k]!=-1,如果第i-1次跳跃不能够到达k那么也不可能在第i次从k跳到j
t=max(t,d[i-1][k]);
}

if(t!=-1)   d[i][j]=t+weight[j];//猴子能从至少一棵树在第i次跳跃中跳到j
ans=max(ans,d[i][j]);//随时都有可能无法再往前跳了,所以每一次填表都应该记录
}
cout<<ans;
}

int main()
{
scanf("%d%d%d",&n,&D,&m);
for(int i=1;i<=n;i++)   scanf("%d%d",&weight[i],&dist[i]);

dp();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息