题解 poj3585 Accumulation Degree

前言:我DP太菜了——明明考了一道很像的题,我还不会QAQ(WA的一声哭了出来)

正文

传送门

首先看到这道题,你可以用一个比较暴力的办法——每次换一个点扫一遍,但是这样会超时,所以我们可以换一种想法——用玄学的DP来解决这类问题

这时你可以来扫两边: 一边先存每个点向子树流向的流量大小,然后用DP再向后推,动态转移方程见程序。你可叫他:二次扫描和换根法

注意点:

1.流量每次应该取最小的,因为你是在三次元,没有河流在没支流的情况下会在经过流量小的后突然变大

2.这题有多组数据

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int MAXN=2*1e5+5;
 6 struct ziji{
 7     int ver,nxt,edge;
 8     #define ver(i) mn[i].ver
 9     #define nxt(i) mn[i].nxt
10     #define edge(i) mn[i].edge 
11 }mn[MAXN<<1]; 
12 int deg[MAXN],head[MAXN],n,m,tot,d[MAXN];
13 bool vis[MAXN];int root=1,x,y,z,fa[MAXN];
14 //fa[i]是以i为根节点时,i可以流向整个树的最大流量
15 //deg[i]是每个点入读个数
16 //d[i]是每个点在第一次扫过后,可以知道的流量 
17 void add(int x,int y,int z){
18     ver(++tot)=y,nxt(tot)=head[x];
19     head[x]=tot,edge(tot)=z;
20 }
21 void dp(int x){
22     //从x出发,流向子树的流量的最大值 
23     vis[x]=1;d[x]=0;
24     for(register int i=head[x];i;i=nxt(i)){
25         int y=ver(i);
26         if(vis[y]) continue;dp(y);
27         if(deg[y]==1) d[x]+=edge(i);
28         //如果这个点只有一个点可以到这里,就只能加上edge[i] 
29         else d[x]+=min(d[y],edge(i));
30         //否则,这个点就要在从上面流过来的流量和这条边可以走的最大流量取min
31         //由三次元可以知道,流量不会凭空变大,只会越来越小 
32     }
33 }
34 void dfs(int x){
35     //求以x为根时,x可以向整个子树去流的最大流量 
36     vis[x]=1;
37     for(register int i=head[x];i;i=nxt(i)){
38         int y=ver(i);if(vis[y]) continue;
39         if(deg[x]==1) fa[y]=d[y]+edge(i);
40         //如果再向下走时,下面的点只有这一个入度
41         //那么这个点的流量就是这个点向他自己的子树和向他的父节点的流量和 
42         else fa[y]=d[y]+min(fa[x]-min(d[y],edge(i)),edge(i));
43         //否则,就是他流向子树的流量与 
44         //理论上可以流向父亲节点的流量和三次元中实际上可以流的较小值之和 
45         //理论上流向父节点的最小值:父节点流向其他节点的流量和它可以在流向父节点的较小值 
46         dfs(y);
47     }
48 } 
49 int main(){
50     int t;scanf("%d",&t);
51     while(t--){
52         memset(vis,0,sizeof(vis));memset(d,0,sizeof(d));
53         memset(deg,0,sizeof(deg));memset(fa,0,sizeof(fa));
54         memset(head,0,sizeof(head));memset(mn,0,sizeof(mn));
55         scanf("%d",&n);root=1;tot=0;
56         if(n==0||n==1){printf("0\n");continue;}
57         for(register int i=1;i<n;i++){
58             scanf("%d%d%d",&x,&y,&z);
59             add(x,y,z);add(y,x,z);
60             deg[x]++,deg[y]++; //统计出入度 
61         }
62             dp(root);fa[root]=d[root];
63             memset(vis,0,sizeof(vis));
64             dfs(root);int ans=0;
65             for(register int i=1;i<=n;i++) ans=max(ans,fa[i]);
66             printf("%d\n",ans);
67     } 
68     return 0;
69 } 
国际惯例:thankyou for your attention

转载于:https://www.cnblogs.com/fallen-down/p/10809697.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值