HDU —— 1176 免费馅饼 的另一种解法(dp)

好长时间没有搞算法了,今天写道题练练手。

免费馅饼

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 62631    Accepted Submission(s): 21932


 

Problem Description

都说天上不会掉馅饼,但有一天gameboy正走在回家的小径上,忽然天上掉下大把大把的馅饼。说来gameboy的人品实在是太好了,这馅饼别处都不掉,就掉落在他身旁的10米范围内。馅饼如果掉在了地上当然就不能吃了,所以gameboy马上卸下身上的背包去接。但由于小径两侧都不能站人,所以他只能在小径上接。由于gameboy平时老呆在房间里玩游戏,虽然在游戏中是个身手敏捷的高手,但在现实中运动神经特别迟钝,每秒种只有在移动不超过一米的范围内接住坠落的馅饼。现在给这条小径如图标上坐标:


为了使问题简化,假设在接下来的一段时间里,馅饼都掉落在0-10这11个位置。开始时gameboy站在5这个位置,因此在第一秒,他只能接到4,5,6这三个位置中其中一个位置上的馅饼。问gameboy最多可能接到多少个馅饼?(假设他的背包可以容纳无穷多个馅饼)

Input

输入数据有多组。每组数据的第一行为以正整数n(0<n<100000),表示有n个馅饼掉在这条小径上。在结下来的n行中,每行有两个整数x,T(0<T<100000),表示在第T秒有一个馅饼掉在x点上。同一秒钟在同一点上可能掉下多个馅饼。n=0时输入结束。

Output

每一组输入数据对应一行输出。输出一个整数m,表示gameboy最多可能接到m个馅饼。
提示:本题的输入数据量比较大,建议用scanf读入,用cin可能会超时。
 

Sample Input

6 5 1 4 1 6 1 7 2 7 2 8 3 0

Sample Output

4

有段时间没有写算法题了,拿来练练手,dp题,准备拿来给学弟讲讲,

写完之后看了下网上题解,大多是从下往上推的,我写的是从上往下推的,因为之前没有写过数塔之类的题,这类题算第一次写。

刚开始想的是建一个dp[100010][13]的数组,一层一层推,后来发现可以缩减为两层,也就是建一个dp[2][13]的数组即可,因为实际上只有上一层的数据需要更新。

下面是状态转移方程:dp[i][j]为在i秒j位置所能得到的最大的馅饼数

dp[i][j] = max(dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1]) + x[i][j]              x[i][j]为在i秒j位置落下的总馅饼数

dp[i][j] = max(dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1])                 在i秒没落下馅饼

解释一下上面的方程,首先明确不管在这一秒有没有落下馅饼,都可以在一秒时间不走或朝左或朝右走一步(max(dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1]) ),所以每过一秒需要更新下一秒所能获得的最大馅饼数,仔细想想会发现,如果一直不落下馅饼,最多过十秒后,dp数组将不会再更新,因为十秒时间你可以从0点走到十点,这时数组里的值都会更新为一样的值且为最大值。

如果在某一秒j位置落下了馅饼,则该位置的值为max(dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1]) + x[i][j],x[i][j]为在i秒j位置落下的总馅饼数,最后一秒的那行数组里的最大值即为最终结果。仔细想想会发现,其实更新时只用到了上一秒的数组,于是可以想到只需两层数组即可实现。

下面上代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

typedef struct {
	int a,b;
}S;
int cmp (S x,S y){
	return x.b < y.b;
}
int M(int a,int b,int c){    //返回最大值
	int sum = a;
	sum = sum > b ? sum : b;
	sum = sum > c ? sum : c;
	return sum;
}
int main(){
	int n,i,j,k;
	int dp[2][13];
	S b[100010];
	while(scanf("%d",&n),n!=0){
		for(i = 1;i <= n;i++){
			scanf("%d%d",&b[i].a,&b[i].b);
			b[i].a++;    //给每个位置加一,可以忽略对边界的处理
                        //因为刚开始在5的位置,加一后为6,总区域为1~11,在前四秒时间如果落在了其他的地方,将不会得到馅饼。所以将其去除。
			if(b[i].b == 1 && (b[i].a>7||b[i].a<5))
				n--,i--;
			if(b[i].b == 2 && (b[i].a>8||b[i].a<4))
				n--,i--;
			if(b[i].b == 3 && (b[i].a>9||b[i].a<3))
				n--,i--;
			if(b[i].b == 4 && (b[i].a>10||b[i].a<2))
				n--,i--;
		}
		sort(b+1,b+n+1,cmp);    //对数据按秒升序排序
		memset(dp,0,sizeof(dp));
		int T = 1;
		int flag;    //该变量确认数组里的值是否相同,可以缩短循环时间,详情见上面解释
		b[0].b = b[1].b;
		for(j = 1;j <= n;j++){
			flag = 1;
                        //每过一秒对数组进行更新
			for(i = b[j-1].b;i != b[j].b && flag;i++,T++){
				flag = 0;
				for(k = 1;k < 12;k++)
					dp[T%2][k] = M(dp[(T-1)%2][k-1],dp[(T-1)%2][k],dp[(T-1)%2][k+1]);
				int s = dp[T%2][1];
				for(k = 2;k < 12;k++)
					if(dp[T%2][k] != s)
						flag = 1;
			}
			dp[(T-1)%2][b[j].a]++;    //落下馅饼加一
		}
		int sum = -1;
		T--;
		for(i = 1;i < 12;i++)
			if(sum < dp[T%2][i])
				sum = dp[T%2][i];
		printf("%d\n",sum);    //寻找最大值输出
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/hunt_er/article/details/81302862
今日推荐