ural(Timus) 1463. Happiness to People!

树DP

题意:输入n和m,表示n个城市,m条无向边,下面一行n个数字,表示每个城市的权值,下面m行是每条边的信息,u,v,w,顶点和边权。问你从一个城市出发,走出一条路线,使得权值和最大,权值和包括这条路线上城市的权值和边的权值和。

题中有一句话出卖了它是个树DP:It turned out that if Petrovich can fly (using one or several flights) from town i to town j, then there is exactly one way to do this.

从一个顶点去另一个顶点如果连通的话只有一条路径,说明这个无向图其实是个无根树,所以又变成了经典问题,在无根树中找两点使它们的路径最长,不过这里要加上点的权值而已,是一样的,另外一个点是要记录路径的,输出路径中经过了多少个点,并且沿路径输出每一个点,如果有多条路径任意一条即可

 

DP思路不说了,可以找前面的题解里面有详细的讲解,说说输出路径。我是分两部分来输出路径的,根到叶子的最大值为一个部分,到叶子的次大值是另一个部分,根据路径输出的要求,前面部分要递归输出,或者可以用一个栈保存,比较懒就用了stl的stack来保存路径,后面的部分不用递归,直接迭代下去,但是为了统计点的个数所以先保存,用了stl的queue来保存路径

整个题目其实不难,注意细节即可

#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
using namespace std;
#define N 50010

typedef long long ll;
typedef pair<ll,ll> pll;
bool vis[N];
vector<pll>a[N];
stack<int>s;
queue<int>q;
ll val[N];
ll dp[N][2];
ll ans;
int n,m,root;
int p[N][2];

void input()
{
    for(int i=0; i<=n; i++) 
        a[i].clear();
    for(int i=1; i<=n; i++) 
        scanf("%lld",&val[i]);
    for(int i=0; i<m; i++)
    {
        int u,v,w;
        pll tmp;
        scanf("%d%d%d",&u,&v,&w);
        tmp.first=v; tmp.second=w; a[u].push_back(tmp);
        tmp.first=u; tmp.second=w; a[v].push_back(tmp);
    }
}

void dfs(int rt)
{
    vis[rt]=true;
    dp[rt][1]=dp[rt][0]=0; 
    p[rt][1]=p[rt][0]=-1;

    int size=a[rt].size();
    for(int i=0; i<size; i++)
    {
        pll tmp = a[rt][i];
        int v = tmp.first;
        int w = tmp.second;
        if(!vis[v])
        {
            dfs(v);
            if(dp[v][1] + w >= dp[rt][1])
            {
                dp[rt][0] = dp[rt][1];
                p[rt][0] = p[rt][1];
                dp[rt][1] = dp[v][1]+w;
                p[rt][1] = v;
            }
            else if(dp[v][1] + w > dp[rt][0])
            {
                dp[rt][0] = dp[v][1] + w;
                p[rt][0] = v;
            }
        }
    }

    dp[rt][1] += val[rt];
    dp[rt][0] += val[rt];
    if( dp[rt][1]+dp[rt][0]-val[rt] > ans)
    {
        root=rt;
        ans = dp[rt][1]+dp[rt][0]-val[rt];
    }
}

int Path1(int rt)
{
    int cc=1;
    int u;
    while(!s.empty()) s.pop();
    s.push(rt);
    for(u=p[rt][1]; u!=-1; u=p[u][1])
    {
        s.push(u);
        cc++;
    }
    return cc;
}

int Path2(int rt)
{
    int cc=0;
    int u;
    while(!q.empty()) q.pop();
    for(u=p[rt][0]; u!=-1; u=p[u][1])
    {
        q.push(u);
        cc++;
    }
    return cc;
}

void solve()
{
    memset(vis,false,sizeof(vis));
    memset(p,-1,sizeof(p));
    ans=-1;   
    /*
    初始化为
    ans=0;
    root=1; //不要初始化为0
    */
    for(int i=1; i<=n; i++)
        if(!vis[i])
            dfs(i);
    printf("%lld\n",ans);

    int c1=Path1(root);
    int c2=Path2(root);
    printf("%d\n",c1+c2);

    while(!s.empty())
    {
        int t=s.top();
        s.pop();
        if(t!=root) printf("%d ",t);
        else        printf("%d",t);
    }
    while(!q.empty())
    {
        int t=q.front();
        q.pop();
        printf(" %d",t);
    }
    printf("\n");
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        input();
        solve();
    }
    return 0;
}

 

posted @ 2013-04-11 10:16  Titanium  阅读(372)  评论(0编辑  收藏  举报