HDU 4679:Terrorist’s destroy 树形DP

Terrorist’s destroy

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4679

题意:

给出一棵树,删除该树的某一条边会得到一个值a(该边的energy值)和一个值b(删除该边得到的两棵子树上的最大路径),求删除哪条边可以使得a*b最小,若存在多个最小值,则选择边号较小的那条。

树上所有相邻节点间的距离都为1。

题解:

可以知道删除某边后两棵子树的最长路径都是由原树直径的两个端点出发的,以任意一个节点为起点跑一边BFS(或者DFS)找到与其最远的叶子节点v1,再以v1为起点找到距离v1最远的叶子节点v2,v1和v2即原树直径的两个端点。

预处理得到v1、v2和其他所有节点的最短距离后深搜得到以某节点v为根的子树和v1的次大值距离以及和v2的次大距离,再跑一遍树形DP就完事了。

= =看错题还以为energy值就是距离,于是乎写的比较麻烦。

      

代码

#include<stdio.h>
#include<string.h>
#include<queue>
#define inf 1000000001
#include<stdlib.h>
using namespace std;
const int N=1e5+1;
int Ev[N*2],Enext[N*2],ENo[N*2],Ew[N*2];
int head[N],cnt;
int dis[N][2],ori[N][3];/*dis[i][0]代表以节点 i 到其叶子的最大距离,dis[i][1]则表示以节点 i 为根的子树的最长路径,ori则代表相对应的节点*/
int dp1[N][2],dp2[N][2],res1,res2;//dp1[i][]代表以节点i为根的子树到直径两个端点的最大值和次大值
bool mark[N];
int a[N][2];//直径左端点和其他所有点的最短距离以及直径右端点和其他所有点的最短距离
void clean(int n)
{
    for(int i=1;i<=n;++i)
    head[i]=-1;
    cnt=0;
}
int mmax(int a,int b)
{
    return a>b?a:b;
}
int mmin(int a,int b)
{
    return a<b?a:b;
}
void addedge(int u,int v,int w,int No)
{
    Ev[cnt]=v;
    Ew[cnt]=w;
    ENo[cnt]=No;
    Enext[cnt]=head[u];
    head[u]=cnt++;
}
void Dfs_FindDis(int id,int u)
{
    int max1=0,max2=0;
    int v1,v2;
    for(int i=head[id];i!=-1;i=Enext[i])
    {
        int v=Ev[i];
        int w=1;
        if(Ev[i]==u)continue;
        Dfs_FindDis(Ev[i],id);
        int disv=dis[v][0]+w;
        if(disv>=max1)
        {
            max2=max1,v2=v1;
            max1=disv,v1=ori[v][0];
        }
        else if(disv>max2)
        {
            max2=disv,v2=ori[v][0];
        }
        disv=dis[v][1];
        if(disv>dis[id][1])
        {
            dis[id][1]=disv;
            ori[id][1]=ori[v][1];
             ori[id][2]=ori[v][2];
        }
    }
    if(!max1)ori[id][0]=ori[id][1]=ori[id][2]=id;
    else
    {
        ori[id][0]=v1;
        dis[id][0]=max1;
        if(max2)
        {
            if(max1+max2>dis[id][1])
            {
                dis[id][1]=max1+max2;
                ori[id][1]=v1;
                ori[id][2]=v2;
            }
        }
        else
        {
            if(max1>dis[id][1])
            {
                dis[id][1]=max1;
                ori[id][1]=v1;
                ori[id][2]=id;
            }
        }
    }
}
struct node
{
    int x;
    int c;
    friend bool operator<(node a,node b)
    {
        return a.c>b.c;
    }
};
void Opt_Dijkstra(int start,int y)
{
    priority_queue<node>q;
    node f,m;
    while(!q.empty())q.pop();
    f.x=start,f.c=a[start][y]=0;
    q.push(f);
    while(!q.empty())
    {
        f=q.top(),m=f,q.pop();
        if(mark[f.x])continue;
        if(f.c!=a[f.x][y])continue;
        mark[f.x]=true;
        for(int i=head[f.x];i!=-1;i=Enext[i])
        {
            int pos=Ev[i];
            if(!mark[pos])
            {
                f=m;
                int w=1;
                int p=f.c+w;
                if(p<a[pos][y])
                {
                    a[pos][y]=p;
                    f.x=pos,f.c=p;
                    q.push(f);
                    f=m;
                }
            }
        }
    }
}
void Dfs1(int id,int fa,int x,int y)
{
    int m1=a[id][0],s1=a[id][0],m2=a[id][1],s2=a[id][1];
    for(int i=head[id];i!=-1;i=Enext[i])
    {
        int v=Ev[i];
        if(v==fa)continue;
        Dfs1(v,id,x,y);
        if(dp1[v][0]>=m1)
        {
            if(m1!=0)s1=m1;
            m1=dp1[v][0];
        }
        else if(dp1[v][1]>s1)s1=dp1[v][1];
        if(dp2[v][0]>=m2)
        {
            if(m2!=0)s2=m2;
            m2=dp2[v][0];
        }
        else if(dp2[v][1]>s2)s2=dp2[v][1];
    }
    if(m1==0)dp1[id][0]=dp1[id][1]=a[id][0];
    else
    {
        dp1[id][0]=m1;
        dp1[id][1]=s1;
    }
    if(m2==0)dp2[id][0]=dp2[id][1]=a[id][1];
    else
    {
        dp2[id][0]=m2;
        dp2[id][1]=s2;
    }
}
void Dfs2(int id,int fa,int smax1,int smax2,int x,int y)
{
    smax1=mmax(smax1,dp1[id][1]);
    smax2=mmax(smax2,dp2[id][1]);
    for(int i=head[id];i!=-1;i=Enext[i])
    {
        int v=Ev[i];
        if(v==fa)continue;
        Dfs2(v,id,smax1,smax2,x,y);
        int cost;
        if(ori[v][0]==x)
        cost=mmax(smax2,dis[v][1])*Ew[i];
        else if(ori[v][0]==y)
        cost=mmax(smax1,dis[v][1])*Ew[i];
        else cost=a[x][1]*Ew[i];
        if(cost==res1)
        {
            res1=cost;
            res2=mmin(res2,ENo[i]);
        }
        else if(cost<res1)
        {
            res1=cost;
            res2=ENo[i];
        }
    }
}
void solve()
{
    int T,n,x,y,Case=0;
    int c;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        clean(n);
        for(int i=1;i<=n;++i)
        {
            a[i][0]=a[i][1]=inf,mark[i]=false;
        }
        for(int i=1;i<n;++i)
        {
            scanf("%d%d%d",&x,&y,&c);
            addedge(x,y,c,i);
            addedge(y,x,c,i);
        }
        memset(dis,0,sizeof(dis));
        Dfs_FindDis(1,1);
        x=ori[1][1];
        y=ori[1][2];
        memset(mark,false,sizeof(mark));
        Opt_Dijkstra(x,0);
        memset(mark,false,sizeof(mark));
        Opt_Dijkstra(y,1);
        res1=inf;
        res2=n+1;
        Dfs1(1,1,x,y);
        Dfs2(1,1,dp1[1][1],dp2[1][1],x,y);
        printf("Case #%d: %d\n",++Case,res2);
    }
}
int main()
{
    solve();
    return 0;
}

  

时间: 2024-11-08 14:33:36

HDU 4679:Terrorist’s destroy 树形DP的相关文章

hdu 4679 Terrorist’s destroy 树形DP

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4679 题意:给定一颗树,每条边有一个权值w,问切掉哪条边之后,分成的两颗树的较大的直径*切掉边的权值最小?如果存在多条边使得结果相同,输出边id最小的 思路: dept一次找出最深的节点,之后以最深的节点出发(rt1)dept找到树的直径(即找到rt2):将路径保存在f[]中: 之后分别从rt1/rt2进行深搜,找到以一个节点为根的树的直径,这样在每次dfs之后,可以求出每条边的一边的最值,这样两次取m

Bestcoder round #65 &amp;&amp; hdu 5593 ZYB&#39;s Tree 树形dp

Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 354    Accepted Submission(s): 100 Problem Description ZYB has a tree with N nodes,now he wants you to solve the numbers of nodes distanced no m

hdu 4514 并查集+树形dp

湫湫系列故事——设计风景线 Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 4539    Accepted Submission(s): 816 Problem Description 随着杭州西湖的知名度的进一步提升,园林规划专家湫湫希望设计出一条新的经典观光线路,根据老板马小腾的指示,新的风景线最好能建成环形,如果没有条件建成环形,

hdu 4118 Holiday&#39;s Accommodation 树形dp

Holiday's Accommodation Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4118 Description Nowadays, people have many ways to save money on accommodation when they are on vacation.One of these ways is exchanging

HDU 1011 Starship Troopers(树形dp+背包)

Starship Troopers Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 13109    Accepted Submission(s): 3562 Problem Description You, the leader of Starship Troopers, are sent to destroy a base of

hdu 5379 Mahjong tree(树形dp)

题目链接:hdu 5379 Mahjong tree 树形dp,每个节点最多有2个子节点为一棵节点数大于1的子树的根节点,而且要么后代的节点值都大于,要么都小于本身(所以tson不为0是,要乘2).对于K个单一节点的子节点,种类数即为全排K!.当一个节点没有兄弟节点时,以这个节点为根结点的子树,根可以选择最大或者最小. #pragma comment(linker, "/STACK:102400000,102400000") #include <cstdio> #inclu

HDU 5758 Explorer Bo(树形DP)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5758 [题目大意] 给出一棵树,每条路长度为1,允许从一个节点传送到任意一个节点,现在要求在传送次数尽量少的情况下至少经过每条路一遍啊,同时最小化走过的路程总长度.输出路程总长度. [题解] 首先,对于传送次数尽量少这个条件,我们很容易发现,当且仅当每次出发点和终止点都是叶节点的时候,是最少的,当然在叶节点无法两两匹配的时候,再多走一条链. 然后就是叶节点的匹配问题,使得匹配后的叶节点连线覆盖全

hdu 4003 Find Metal Mineral 树形DP

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4003 Humans have discovered a kind of new metal mineral on Mars which are distributed in point‐like with paths connecting each of them which formed a tree. Now Humans launches k robots on Mars to collect

hdu 4123 Bob’s Race(树形dp+RMQ)

题目链接:hdu 4123 Bob's Race 题目大意:一个城镇有N个住户,N-1条路连接两个住户,保证N个住户联通,M次询问,给定N条边的信息,包括连 接的住户序号以及路的长度.然后是M次询问,每次询问Q,要求找到最长的连续序号,使得Max(dis[i]) - Min(dis[i]) ≤ Q(l≤i≤r),输出最大的r-l+1.dis[i]为从第i个住户出发,不重复走过路能移动的最远距离. 解题思路:树形dp,通过两次dfs,第1次处理出每个节点中孩子节点移动的最长距离和第二长距离,第2次