暑期dp46道(11)——HDOJ 1422重温世界杯 dp+记忆化搜索

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1422




解题思路:这题刚开始用的暴力

(一)首先第i个城市的裸剩余费用:data[i]=a-b(可能为0或负)

(二)然后按照游览方向递推直到当前所在城市的裸费用不足0记录之前已浏览的城市数目,

(三)枚举i:1->n求最大值

不出预料:TLE

所以就想到了 昨天一道dfs+记忆化搜索的题目,想到用一个need[]记录从当前城市出发到最后一个因费用不够被强制退出时的裸费用之和,所以need[]一般为负(所有城市游览都顺利的情况除外)

所以这题要从后往前遍历:

依靠记录的dp[]和need[]“平移”,降低时间复杂度。

detail:

1.如果出发点(第i个城市)裸费用为负,则 

 	if(temp<0)
        {
            dp[i]=0;
            need[i]=temp;
        }

2.data[i]>=0向后搜索j:i+1->i+1(处理边界问题)

如果

dp[j]存在,凭借dp[j]和need[j]移动跳过从j出发即可顺利浏览的部分

 		if(dp[j])
                {
                    temp+=need[j];
                    j=T(j+dp[j],n);
                }


继续遍历:如果费用不足记录dp[i](方向问题)

	 	if(temp<0)
                {
                    need[i]=temp; 
                    if(j>i)
                        dp[i]=j-i;    
                    else
                        dp[i]=n+j-i;
                        break;
                } 

PS:一定要注意边界问题,刚开始用的求余,比较麻烦,后来写了一个处理边界问题

#define T(a,b) ((a>b)?a-b:a)

Code:

#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
using namespace std;
#define Max(a,b) ((a>b)?a:b)
#define M(a) memset(a,0,sizeof(a))
#define T(a,b) ((a>b)?a-b:a)
#define debug 0
const int maxn=100000+5;
int data[maxn],dp[maxn],need[maxn];
int n;
void Do()
{
	int j,ans=0;
	for(int i=n;i>=1;i--)//从后往前
	{
		int temp=data[i];
		if(temp<0)
		{
			dp[i]=0;
			need[i]=temp;
		}
		else
		{
			for(j=T(i+1,n);j!=i;j=T(j+1,n))
			{
				if(dp[j])
				{
					temp+=need[j];
					j=T(j+dp[j],n);
				}
				else
				{
					temp+=data[j];
				}
				if(temp<0)
				{
					need[i]=temp; 
					if(j>i)
						dp[i]=j-i;	
					else
						dp[i]=n+j-i;
						break;
				} 
			}
			if(j==i)
			{
				printf("%d\n",n);
				return;
			}
		}
		ans=Max(ans,dp[i]);
	}
	printf("%d\n",ans);
}
int main()
{
#if debug
	freopen("in.txt","r",stdin);
#endif //debug
	int a,b;
	while(~scanf("%d",&n))
	{
		M(dp);
		M(need);
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d",&a,&b);
		//	printf("%d %d\n",a,b);
			data[i]=a-b;
		}
		Do();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值