HDU--3721[Building Roads] 枚举+求最长路O(N^2)

47 篇文章 1 订阅
25 篇文章 0 订阅
题意:

给一棵树,可以移动树上的一条边,但是必须保证移动之后的图还是一棵树,问如何移动才能使得移动之后的树的直径最短。


思路:


先在原树上求直径,然后枚举直径上的边(因为要使其直径变短只可能删除直径上的某条边)。u->v.这样原树就被拆成两个子树。


(1):分别求出两个子树的直径du和dv.


(2):在每棵子树中求一点x。使这点到子树中的其他任意一点的最长距离最短。由分析可知,这一点x一定在子树的直径上,且在直径的中心两点中的其中一点(证明如下)。


(3):分别求出两棵子树的那两个关键点x后。更新答案ans_l=min(ans_l,max(du,dv,ss+ee+w)).   //ss,ee是两棵子树的关键点x到其他点的最长距离;w是当前枚举边的长度


证明如下:  摘自 http://hi.baidu.com/dispossessed/blog/item/660a8a294b9d62459922ed5f.html

假设要求的点不是直径上面的点,那么由于原图是一棵树,取这个点到直径的最短路径,交直径与一个点,这个点到其他点的最长距离一定大于交点到其他点的最长距离,这样

就说明了要求的点一定是直径上面的点,既然是直径上面的点,因为直径是一条链,显然它一定是中心附近的点。



PS.一开始的复杂度是O(N^3),TLE,后然看了别人的题解后发现“关键点”只可能在直径的中心附近的两个点中的一个。这样就把复杂度降到了O(N^2).


CODE:

/*枚举+求最长路O(N^2)*/
/*AC代码:170ms*/
#include <iostream>
#include <cstdio>
#include <memory.h>
#include <algorithm>
#include <queue>
#define MAXN 2505
#define INF 1e8
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
using namespace std;
struct edge
{
    int u,v,w,next;
    bool ok;
}E[3*MAXN];
int head[MAXN];
int ecnt;
int dis[MAXN],road[MAXN],pre[MAXN],cnt;
bool vis[MAXN],col[MAXN];
int N,ans_l,cas,lenc;
queue<int>Q;

void Insert(int u,int v,int w)
{
    E[ecnt].u=u;
    E[ecnt].v=v;
    E[ecnt].w=w;
    E[ecnt].ok=true;
    E[ecnt].next=head[u];
    head[u]=ecnt++;
}
void Print()
{
    int i;
    for(i=1;i<=N;i++)
        printf("%d ",col[i]);
    printf("**\n");
}
void Init()
{
    int i,u,v,w;
    memset(head,-1,sizeof(head));ecnt=0;
    scanf("%d",&N);
    for(i=1;i<N;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        u++;v++;
        Insert(u,v,w);
        Insert(v,u,w);
    }
}
int BFS(int s)
{
    int i,u,v,w,res;
    while(!Q.empty()) Q.pop();
    memset(vis,false,sizeof(vis));
    memset(dis,-1,sizeof(dis));
    memset(pre,-1,sizeof(pre));
    vis[s]=true;
    dis[s]=0;
    Q.push(s);
    while(!Q.empty())
    {
        u=Q.front();Q.pop();
        for(i=head[u];i!=-1;i=E[i].next)
        {
            if(E[i].ok==false) continue;
            v=E[i].v;w=E[i].w;
            if(!vis[v])
            {
                dis[v]=dis[u]+w;
                pre[v]=i;
                vis[v]=true;
                Q.push(v);
            }
        }
    }
	res=0;
	for(i=1;i<=N;i++)
	{
		if(dis[i]==-1) continue;
		if(dis[i]>res) res=dis[i];
	}
	return res;
}
int Go(int x,bool flag)
{
    int i,u,v,s,e,Max,l,temp[MAXN],tcnt;
    BFS(x);
    s=0;Max=0;lenc=INF;
    for(i=1;i<=N;i++)//染色
    {
        if(vis[i])
        {
            col[i]=flag;
            if(dis[i]>Max)
            {s=i;Max=dis[i];}
        }
    }
    BFS(s);
    e=0;Max=0;
    for(i=1;i<=N;i++)
    {
        if(vis[i])
        {
            if(dis[i]>Max)
            {e=i;Max=dis[i];}
        }
    }
	int sum=0;
	tcnt=0;
    u=e;
    while(true)
    {
        l=pre[u];
        if(l==-1) break;
        temp[tcnt++]=l;
		sum+=E[l].w;
        u=E[l].u;
    }
	sum/=2;
	int len=0;
	for(i=0;i<tcnt;i++)
	{
		len+=E[temp[i]].w;
		if(len>sum) break;
	}
	//最好的点就在这两个点之间
	if(tcnt==0)//避免RE
	{
		lenc=0;
		return Max;
	}
	u=E[temp[i]].u;
	v=E[temp[i]].v;
	int lu=BFS(u);
	int lv=BFS(v);
	//printf("&%d %d %d %d %d\n",tcnt,u,v,lu,lv);
	lenc=min(lu,lv);
    return Max;
}
void fuck(int ith)
{
    int i,u,v,s,e,ss,ee,ds,de,dw,Max;
    memset(col,false,sizeof(col));
    //printf("%d %d \n",road[ith],road[ith]^1);
    E[road[ith]].ok=E[road[ith]^1].ok=false;
    s=E[road[ith]].u;
    e=E[road[ith]].v;
    ds=Go(s,1); ss=lenc;
    de=Go(e,0); ee=lenc;

    //Print();

    dw=ss+ee+E[road[ith]].w;
    int res=max(dw,max(ds,de));
	
    //printf("*%d %d %d %d %d %d\n",s,e,ans_l,ds,de,dw);
    ans_l=min(ans_l,res);
    E[road[ith]].ok=E[road[ith]^1].ok=true;
}
void Run()
{
    int i,s,e,u,Max,l;
    BFS(1);
    s=1;Max=dis[1];
    for(i=2;i<=N;i++)
    {
        if(dis[i]>Max)
        {s=i;Max=dis[i];}
    }
    BFS(s);
    e=1;Max=dis[1];
    for(i=2;i<=N;i++)
    {
        if(dis[i]>Max)
        {e=i;Max=dis[i];}
    }
    ans_l=Max;
    cnt=0;
    u=e;
    while(true)
    {
        l=pre[u];
        if(l==-1) break;
        road[cnt++]=l;
        u=E[l].u;
    }
    for(i=0;i<cnt;i++)
    {
        fuck(i);
    }
}
void Solve()
{
    int i;
    Run();//求出最长路径
    printf("Case %d: %d\n",cas++,ans_l);
}
int main()
{
    int T;
    cas=1;
    scanf("%d",&T);
    while(T--)
    {
        Init();
        Solve();
    }
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__简言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值