2011 Asia Fuzhou Regional Contest-1003 hdu4123 Bob’s Race

同样北大的题,题目很简单,想法很简单也很容易实现,但却要优化好,否则很容易超时。

题目描述:Bob要办一场赛跑比赛,有N个房子互相相连,任意两个房子间有且仅有一天路(树形结构),有N个人分别从这N个房子出发,要求每个人都跑到尽量远的房子,但每个人只能经过每条路一次,不同人之间跑的路程会有差距,Bob想知道最多有多少个连续的人他们中最远的路程减最小的路程不大于P。最多有50000个点,500次询问。

解题思路:

    1.  先用树形DP求出从每个房子出发跑的最远的路程。dp[s][0]代表从s出发往下走最远跑的路程,dp[s][1]代表从s出发往上跑最远的路程

        状态转移方程: dp[s][0]=max(dp[son[i]][0]+len[s][i])

        dp[s][1]=max(dp[par[s]][1],max(dp[son[par[i]]+len[par][i]]))+len[par][s];

    2.  优化求出最大值最小值只差不大于p的最大连续序列长度


#include<iostream>
#include<vector>

using namespace std;

vector<int> edge[50005];
vector<int> len[50005];
const int MAXDIS=10000010;
int dis[50005],par[50005],res[50005],dp[50005][2];

void dfs(int curr)   // 从上往下递归求dp[curr][0]
{
    int lenth=edge[curr].size();
    dp[curr][0]=0;
    for(int i=0; i<lenth; i++)
    {
        if(edge[curr][i]!=par[curr])
        {
            par[edge[curr][i]]=curr;
            dfs(edge[curr][i]);
            if(dp[edge[curr][i]][0]+len[curr][i]>dp[curr][0])
            {
                dp[curr][0]=dp[edge[curr][i]][0]+len[curr][i];
            }
        }
    }
}

void b_dfs(int curr)   // 从下往上推dp[curr][1]
{
    if(dp[curr][1]!=-1) return;
    dp[curr][1]=0;
    int rec,tmp=par[curr];
    int lenth=edge[tmp].size();
    for(int i=0; i<lenth; i++)
    {
        if(edge[tmp][i]!=curr&&edge[tmp][i]!=par[tmp])
        {
            if(len[tmp][i]+dp[edge[tmp][i]][0]>dp[curr][1])
            {
                dp[curr][1]=len[tmp][i]+dp[edge[tmp][i]][0];
            }
        }
        else if(edge[tmp][i]==curr) rec=len[tmp][i];
    }
    b_dfs(tmp);
    if(dp[tmp][1]>dp[curr][1]) dp[curr][1]=dp[tmp][1];
    dp[curr][1]+=rec;
}

int main()
{
    int n,m,a,b,c,resVal,recMin,recMax;
    while(scanf("%d%d",&n,&m)&&n)
    {
        for(int i=1; i<=n; i++)
        {
            edge[i].clear();
            len[i].clear();
        }
        memset(par,0,sizeof(par));
        for(int i=1; i<n; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            edge[a].push_back(b);
            edge[b].push_back(a);
            len[a].push_back(c);
            len[b].push_back(c);
        }
        dfs(1);
        dp[1][1]=0;
        for(int i=2; i<=n; i++) dp[i][1]=-1;
        for(int i=2; i<=n; i++)
        {
            if(edge[i].size()==1)  // 表示i是叶子节点
            {
                b_dfs(i);
            }
        }
        for(int i=1; i<=n; i++) dis[i]=max(dp[i][0],dp[i][1]);
        for(int i=0; i<m; i++)
        {
            scanf("%d",&a);
            res[1]=1;
            recMin=recMax=dis[1];
            for(int i=2; i<=n; i++)
            {
                if(dis[i]>recMax) recMax=dis[i];
                else if(dis[i]<recMin) recMin=dis[i];
                if(recMax-recMin>a)
                {
                    recMax=recMin=dis[i];
                    res[i]=1;
                    for(int j=i-1; j>0; j--)
                    {
                        if(dis[j]>recMax)
                        {
                            if(dis[j]-recMin>a) break;
                            else
                            {
                                recMax=dis[j];
                                res[i]++;
                            }
                        }
                        else if(dis[j]<recMin)
                        {
                            if(recMax-dis[j]>a) break;
                            else
                            {
                                recMin=dis[j];
                                res[i]++;
                            }
                        }
                        else res[i]++;
                    }
                }
                else res[i]=res[i-1]+1;
            }
            resVal=res[1];
            for(int i=2; i<=n; i++)
            {
                if(res[i]>resVal) resVal=res[i];
            }
            printf("%d\n",resVal);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值