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; }