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;
}
浙公网安备 33010602011771号