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;

}





 

你可能感兴趣的:(APP)