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

针对一道经典的最长上升子序列问题,本文介绍了三种不同的解决方法,包括直接的状态转移方程实现导致的时间超限问题,通过构建二维数组进行优化,并最终采用逆向思维简化问题,实现了有效的算法设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接


思路:

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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值