题目链接: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;
}