1463. Happiness to People! 夜

http://acm.timus.ru/problem.aspx?space=1&num=1463

树形DP  

此题有个陷阱   就是图的本身是一个森林 不是树

由于所有 happiness 的值都为非负 所有最优路径一定是从一个叶子节点到另一个叶子节点(当然 两个节点可能一样)

思路:

包含一个节点 和 两个节点的 树特殊处理

对应3个节点以及3个节点以上的树 一定能找到一个初度 大于等于2 的点 以此点为根节点更新

对于这个树  dfs  记录每个点向下的第一最优路径选择 和第二最优路径选择

然后不断向上更新

最后 把所有向下路径至少两条的节点 将此类节点的两个最优路径 连起来的路径进行和最终答案比较 择优而选

刚开始用 vector 建图 结果超内存了 看来 vector 的时间和空间占用都比较大

代码及其注释:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<string>
#include <iomanip>
using namespace std;
const int N=50005;
int head[N],I;
struct node
{
    int j,h,next;
}side[N*2];//双向边
int out[N],sub[N],next1[N],next2[N];//初度 ,子树最优长度 ,第一选择路径,第二选择路径
vector<int>ans;
int a[N];
void Add(int i,int j,int h)
{
    side[I].j=j;
    side[I].h=h;
    side[I].next=head[i];
    head[i]=I++;
}
int Optimal(int i)//以此点为根节点的树中 最优路径长度
{
    if(out[i]==0)
    return a[i];
    if(out[i]==1)
    return a[i]+a[side[head[i]].j]+side[head[i]].h;
    return a[i]+side[next1[i]].h+sub[side[next1[i]].j]+side[next2[i]].h+sub[side[next2[i]].j];
}
int Fhappy(int x,int i)//从x节点选择i路径向下的路径长度
{
    return (side[i].h+sub[side[i].j]);
}
int dfs(int x,int pre)
{
    for(int t=head[x];t!=-1;t=side[t].next)
    {
        int l=side[t].j;
        if(l==pre)
        continue;
        dfs(l,x);
        if(next2[x]==-1||Fhappy(x,t)>Fhappy(x,next2[x]))//更新最优选择路径
        next2[x]=t;
        else
        continue;
        if(next1[x]==-1||Fhappy(x,next2[x])>Fhappy(x,next1[x]))//更新最优选择路径
        swap(next2[x],next1[x]);
    }
    sub[x]=a[x];
    if(next1[x]!=-1)
    sub[x]+=Fhappy(x,next1[x]);
    return sub[x];
}
int main()
{
    //freopen("data.txt","r",stdin);
    int n,m;
    while(scanf("%d %d",&n,&m)!=EOF)
    {
       memset(head,-1,sizeof(head));I=0;
       memset(next1,-1,sizeof(next1));
       memset(next2,-1,sizeof(next2));
       memset(out,0,sizeof(out));
       for(int i=1;i<=n;++i)
       scanf("%d",&a[i]);
       int l,r,h;
       while(m--)
       {
           scanf("%d %d %d",&l,&r,&h);
           Add(l,r,h);
           Add(r,l,h);
           ++out[l];
           ++out[r];
       }
       int k=-1;
       for(int i=1;i<=n;++i)
       {
           if(out[i]>=2&&next1[i]==-1)
           {
               dfs(i,-1);
               if(k==-1||Optimal(i)>Optimal(k))
               k=i;
           }
       }
       for(int i=1;i<=n;++i)
       {
           if(out[i]>=3)
           {
               if(k==-1||Optimal(i)>Optimal(k))
               k=i;
           }else if(out[i]==0)//只有一个点才情况
           {
               if(k==-1||Optimal(i)>Optimal(k))
               k=i;
           }else if(out[i]==1&&out[side[head[i]].j]==1)//只有两个点的情况
           {
               if(k==-1||Optimal(i)>Optimal(k))
               k=i;
           }
       }
       if(out[k]<=1)
       {
           printf("%d\n",Optimal(k));
           if(out[k]==0)
           {printf("1\n");printf("%d\n",k);}
           else
           {printf("2\n");printf("%d %d\n",k,side[head[k]].j);}
           continue;
       }
       ans.clear();
       ans.push_back(k);//将最优路径存到 ans 里面
       int tmp=side[next1[k]].j;
       do{
         ans.push_back(tmp);
         if(next1[tmp]==-1)
         break;
         tmp=side[next1[tmp]].j;
       }while(true);
       tmp=side[next2[k]].j;
       do{
         ans.insert(ans.begin(),tmp);
         if(next1[tmp]==-1)
         break;
         tmp=side[next1[tmp]].j;
       }while(true);
       printf("%d\n",Optimal(k));
       printf("%d\n",ans.size());
       for(unsigned int i=0;i<ans.size();++i)
       printf("%d ",ans[i]);
       printf("\n");
    }
    return 0;
}


原文地址:https://www.cnblogs.com/liulangye/p/2744299.html