hdu1176 免费馅饼 两种动态规划 三段程序(分别为TLE,AC,AC)

时间:2022-02-17 18:44:36

题目链接


思路:

1)

做完了最长上升子序列(hdu5748)和一道类似的hdu1087 之后,被推荐了这道题目,于是自然而然地就想到了一个状态转移方程。

从之前任意某一秒所在的位置到当前所在的位置,这之间的距离如果能在这段时间内到达的话,就纳入考虑范围。

sum[i] = max(sum[j]+1) ,j<=i (先快排)并且 abs(x[i]-x[j])<=T[i]-T[j]

复杂度是n^2

然后毫无悬念地TLE了


TLE代码如下:

//1176.cpp

#include <cstdio>
#define maxn 110000
#include <cmath>
#include <iostream>
using namespace std;
int x[maxn],T[maxn],sum[maxn];
void qsort(int l,int r,int x[maxn],int T[maxn]);
int main()
{
int n,i,j,maxx;
while (scanf("%d",&n) && n!=0)
{
for (i=1;i<=n;++i)
scanf("%d %d",&x[i],&T[i]);
x[0] = 5;
T[0] = 0;
sum[0] = 0;
qsort(1,n,x,T);
for (i=1;i<=n;++i)
{
maxx = 0;
for (j=0;j<i;++j)
if (abs(x[i]-x[j])<=T[i]-T[j])
maxx = max(maxx,sum[j]+1);
sum[i] = maxx;
}
maxx = 0;
for (i=0;i<=n;++i)
maxx = max(maxx,sum[i]);
printf("%d\n",maxx);
}
return 0;
}
void qsort(int l,int r,int x[maxn],int T[maxn])
{
int key,keyy;
if (l<r)
{
int i = l, j = r, key = T[l], keyy = x[l];
while (i<j)
{
while(i<j && T[j]>=key)
j--;
if(i<j)
{
T[i] = T[j];
x[i++] = x[j];
}
while(i<j && T[i]<key)
i++;
if(i<j)
{
T[j] = T[i];
x[j--] = x[i];
}
}
T[i] = key;
x[i] = keyy;
qsort(l,i-1,x,T);
qsort(i+1,r,x,T);
}
}


2)

后来仔细想了想 abs(x[i]-x[j])<=T[i]-T[j] 这个条件,感觉很形象啊,如果画成二维的图,没错就是大家所说的那个数塔。

开一个dp[i][j],表示第j秒在第i位置处能接到的馅饼总数。

然而还要开一个数组,表示第j秒能否到达第i位置处(虽然这只对前几秒有意义......却是必不可少的)


AC代码如下:

//11762.cpp

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxx 11
#define maxn 110000
int a[maxx][maxn],dp[maxx][maxn];
bool can[maxx][maxn];
int main()
{
int ans,n,maxT,i,j,x,y;
memset(can,0,sizeof(can));
for (i=0;i<=5;++i)
for (j=5-i;j<=5+i;++j)
can[j][i] = true;
for (i=0;i<=10;++i)
for (j=6;j<=maxn;++j)
can[i][j] = true;
while (scanf("%d",&n) && n!=0)
{
memset(a,0,sizeof(a));
memset(dp,0,sizeof(dp));
maxT = 0;
for (i=0;i<n;++i)
{
cin >> x >> y;
++a[x][y];
maxT = max(maxT,y);
}
ans = 0;
for (i=4;i<=6;++i)
{
dp[i][1] = a[i][1];
ans = max(ans,dp[i][1]);
}
for (i=2;i<=maxT;++i)
{
if (can[0][i])
dp[0][i] = max(dp[0][i-1],dp[1][i-1])+a[0][i];
if (can[10][i])
dp[10][i] = max(dp[10][i-1],dp[9][i-1])+a[10][i];
for (j=1;j<=9;++j)
if (can[j][i])
{
dp[j][i] = max(dp[j+1][i-1],dp[j][i-1]);
dp[j][i] = max(dp[j][i],dp[j-1][i-1]);
dp[j][i] += a[j][i];
}
for (j=0;j<=10;++j)
ans = max(ans,dp[j][i]);
}
printf("%d\n",ans);
}

return 0;
}



3)

后来看了discuss。

将整个过程倒过来想的话,就是起点随意,终点在5。这样的话在任意时间到达任意位置都是可以的,于是就可以省去判断的过程了。

就又耐心地写了一遍。

尽管最后跑下来,和2的时间一样,都是358MS...

但我觉得这个过程总是值得我来写一篇博客了。


AC代码如下:

//11763.cpp

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxx 11
#define maxn 110000
int a[maxx][maxn],dp[maxx][maxn];
int main()
{
int ans,n,maxT,i,j,x,y;
while (scanf("%d",&n) && n!=0)
{
memset(a,0,sizeof(a));
memset(dp,0,sizeof(dp));
maxT = 0;
for (i=0;i<n;++i)
{
cin >> x >> y;
++a[x][y];
maxT = max(maxT,y);
}
for (i=0;i<=10;++i)
dp[i][maxT] = a[i][maxT];
for (i=maxT-1;i>=1;--i)
{
dp[0][i] = max(dp[0][i+1],dp[1][i+1])+a[0][i];
dp[10][i] = max(dp[10][i+1],dp[9][i+1])+a[10][i];
for (j=1;j<=9;++j)
{
dp[j][i] = max(dp[j+1][i+1],dp[j][i+1]);
dp[j][i] = max(dp[j][i],dp[j-1][i+1]);
dp[j][i] += a[j][i];
}
}
ans = 0;
for (i=4;i<=6;++i)
ans = max(ans,dp[i][1]);
printf("%d\n",ans);
}

return 0;
}