同样北大的题,题目很简单,想法很简单也很容易实现,但却要优化好,否则很容易超时。
题目描述: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;
}