• 【HDU3721】枚举+最长路


    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3721

    题意:给你一颗n个节点n-1条边的树,每条边都有一个权值,现在让你任意移动一条边然后把这条边连接到任意两个点上,最后问你怎样移动才能使树上相距最远的两个点距离最小。

    思路:先求出树的最长路,然后枚举移动最长路上的所有边,移走这条边后,原树必定分为不连接的两颗子树,分别求这两颗子树的最长路,然后分别找到两颗子树最长路上靠近中点的点,把这两个点连上刚刚从母树移走的边,再求一遍母树最长路,比较所有结果取最优解即可。

    注意每次枚举移动后都要把图复原然后继续枚举。

      1 #pragma comment(linker, "/STACK:1024000000,1024000000")
      2 #include <iostream>
      3 #include <cstdio>
      4 #include <cstring>
      5 #include <queue>
      6 #include <algorithm>
      7 using namespace std;
      8 
      9 const int maxn=2555;
     10 const int oo=0x3fffffff;
     11 int visit[maxn], pre[10*maxn];
     12 int reach[10*maxn], flow[10*maxn], head[maxn], next[10*maxn];
     13 int stack[10*maxn], sa[10*maxn], sb[10*maxn];
     14 int color[10*maxn];
     15 int st, sd, ans, edge;
     16 
     17 struct Node
     18 {
     19     int u, e;
     20     int dis;
     21 };
     22 queue<Node>q;
     23 
     24 inline void addedge(int u, int v, int c)
     25 {
     26     reach[edge]=v, flow[edge]=c, next[edge]=head[u], head[u]=edge++;
     27     reach[edge]=u, flow[edge]=c, next[edge]=head[v], head[v]=edge++;
     28 }
     29 
     30 void bfs(int ss,int op)
     31 {
     32     memset(visit,0,sizeof(visit));
     33     while(!q.empty()) q.pop();
     34     Node s, p;
     35     s.u=ss, s.dis=0, s.e=-1, sd=-1, st=ss;
     36     q.push(s);
     37     visit[ss]=1;
     38     int maxx=0, pos;
     39     while(!q.empty())
     40     {
     41         p=q.front();
     42         q.pop();
     43         for(int i=head[p.u]; i>=0; i=next[i])
     44         {
     45             if(color[i]||color[i^1]) continue;
     46             int v=reach[i], val=flow[i];
     47             s.u=v, s.dis=p.dis+val, s.e=i;
     48             if(!visit[s.u])
     49             {
     50                 visit[s.u]=1;
     51                 pre[s.e]=p.e;
     52                 if(s.dis>maxx)
     53                 {
     54                     st=s.u;
     55                     maxx=s.dis;
     56                     sd=s.e;
     57                 }
     58                 q.push(s);
     59             }
     60         }
     61     }
     62     ++op;
     63     if(op==1) bfs(st,op);
     64     else  ans=maxx;
     65 }
     66 
     67 int cal(int s[], int n, double len)  ///找最靠近中点的点
     68 {
     69     int sum=0;
     70     for(int i=0; i<n; i++)
     71     {
     72         sum+=flow[s[i]];
     73         if(sum>=len)
     74         {
     75             if(sum-len<=len-(sum-flow[ s[i] ])) return reach[ s[i]^1 ];
     76             else return reach[ s[i] ];
     77         }
     78     }
     79 }
     80 
     81 int Solve(int n)
     82 {
     83     int MIN=oo;
     84     memset(color,0,sizeof(color));
     85     memset(pre,-1,sizeof(pre));
     86     bfs(1,0);
     87     int top=0;
     88     while(sd!=-1) stack[top++]=sd,sd=pre[sd];
     89     for(int i=0; i<top; i++)  ///枚举最长路上的所有边
     90     {
     91         int x=stack[i], na=0, nb=0;
     92         color[x]=1;
     93         for(int j=0; j<edge; j++) pre[j]=-1;
     94         bfs(reach[x],0);
     95         while(sd!=-1) sa[na++]=sd, sd=pre[sd];
     96         int u=cal(sa,na,1.0*ans/2);
     97         if(!na) u=reach[x];
     98         for(int j=0; j<edge; j++) pre[j]=-1;
     99         bfs(reach[x^1],0);
    100         while(sd!=-1) sb[nb++]=sd,  sd=pre[sd];
    101         int v=cal(sb,nb,1.0*ans/2);
    102         if(!nb) v=reach[x^1];
    103         addedge(u,v,flow[x]);
    104         bfs(1,0);
    105         MIN=min(MIN,ans);
    106         color[edge-1]=1;    ///注意把新加的边移除,
    107         color[x]=0;
    108     }
    109     return MIN;
    110 }
    111 
    112 int main()
    113 {
    114     int n, T, tcase=0;
    115     cin >> T;
    116     while(T--)
    117     {
    118         cin >> n;
    119         edge=0;
    120         memset(head,-1,sizeof(head));
    121         for(int i=1; i<n; i++)
    122         {
    123             int a, b, c;
    124             scanf("%d%d%d",&a,&b,&c);
    125             addedge(a,b,c);
    126         }
    127         int tmp=Solve(n);
    128         printf("Case %d: %d
    ",++tcase,tmp);
    129     }
    130 }
    131 /*
    132 10
    133 9
    134 0 1 1
    135 1 2 1
    136 2 3 1
    137 2 4 1
    138 0 5 1
    139 5 6 1
    140 5 7 1
    141 7 8 1
    142 4
    143 0 1 2
    144 1 2 2
    145 2 3 2
    146 5
    147 0 1 1
    148 1 2 2
    149 2 3 3
    150 3 4 4
    151 */
    View Code
  • 相关阅读:
    Java实现 LeetCode 402 移掉K位数字
    Java实现 LeetCode 402 移掉K位数字
    Java实现 LeetCode 401 二进制手表
    Java实现 LeetCode 401 二进制手表
    wpa_supplicant使用笔记-wpa_cli iwconfig
    Linux下的定时器:alarm()与setitimer()
    在 Windows 下远程桌面连接 Linux
    assert()函数用法总结
    linux下svn修改用户名和密码
    VirtualBox
  • 原文地址:https://www.cnblogs.com/kane0526/p/3310915.html
Copyright © 2020-2023  润新知