Another Postman Problem

Another Postman Problem
Chinese Postman Problem is a very famous hard problem in graph theory. The problem is to find a shortest closed path or circuit that visits every edge of a (connected) undirected graph. When the graph has an Eulerian Circuit (a closed walk that covers every edge once), that circuit is an optimal solution.
This problem is another version of Postman Problem. Assume there are n towns and n-1 roads, and there is a unique path between every pair of towns. There are n-1 postmen in every town, and each postman in one town regularly sends mails to one of the other n-1 towns respectively. Now, given the length of each road, you are asked to calculate the total length that all the postmen need to travel in order to send out the mails.
For example, there are six towns in the following picture. The 30 postmen should totally travel 56. The postmen in town 0 should travel 1, 2, 2, 2, 3 respectively, the postmen in town 1 should travel 1, 1, 1, 1, 2 respectively, the postmen in town 2 should travel 1, 1, 2, 2, 2 respectively, the postmen in town 3 should travel 1, 2, 3, 3, 3 respectively, the postmen in town 4 should travel 1, 2, 2, 2, 3 respectively, and the postmen in town 5 should travel 1, 2, 2, 2, 3 respectively. So the total distance is 56.
Input
The first line of the input contains an integer T(T≤20), indicating the number of test cases. Each case begins with one integer n(n≤100,000), the number of towns. In one case, each of the following n-1 lines describes the length of path between pair a and b, with the format a, b, c(1≤c≤1000), indicating that town a and town b are directly connected by a road of length c. Note that all the n towns are numbered from 0 to n-1.
Output
For each test case, print a line containing the test case number (beginning with 1) and the total sum of the length that all postmen should travel.
Sample Input
1
6
0 1 1
1 2 1
2 3 1
1 4 1
1 5 1
Sample Output
Case 1: 56

题目大意:求每个点到其他所有点的总距离。
思路:可以转化为一共经过每个边多少次。对于每一个边,经过这个边的次数为sum_lsum_r2(这个边的左子树端点个数sum_l和右子树端点个数sum_r)。
关键点:求每条边的子树端点个数。
用dfs遍历图求解

vector<int>edges[MAX_N],v[MAX_N];
//edges[i]存i这个节点所连接的其他节点,v[i]存i这个节点和其他所连节点的权值 
bool can[MAX_N];
//检查是否遍历过,防止两个节点来回遍历然后凉凉,而且注意初始化
int d[MaAX_N][MAX_N]; 
long long dfs(int now){//返回now这个端点的子树端点数 
//假设一开始是dfs(0),now在0的左边,dfs(now)就是返回的now这个点的左子树的总端点数加他本身 
    can[now]=true;
    long long ans=1;
    for(i=0;i<edges[now].size();i++){
     if(can[edges[now][i]])
     continue;
     long long mm=dfs(now);
     ans+=mm;//把跟它相连的端点的子树端点和都加上就是整个now左子树(或者右子树)的端点数之和 
     d[now][edges[now][i]]=2*mm*(n-mm);
     //d[i][j]就是求的i到j这条边的总经过数,一般直接加起来就行,否则存着会内存超限 
 }
 return ans;
}
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
vector<int>edges[101000],v[101000];
int can[101000];
int n;
long long sum;
long long dfs(int now){
 int i;
 can[now]=1;
 long long ans=1;
 for(i=0;i<edges[now].size();i++){
  if(can[edges[now][i]])
  continue;
  long long mm=dfs(edges[now][i]);
  ans+=mm;
  sum+=(long long)2*mm*(n-mm)*v[now][i];
 }
 return ans;
}
int main(void){
 int i,T,t,a,b,c;
 cin>>T;
 t=T;
 while(T--){
  scanf("%d",&n);
  sum=0;
  for(i=0;i<n;i++){
   edges[i].clear();
   v[i].clear();
  }
  memset(can,0,sizeof(can));
  for(i=0;i<n-1;i++){
   scanf("%d%d%d",&a,&b,&c);
   edges[a].push_back(b);
   v[a].push_back(c);
   edges[b].push_back(a);
   v[b].push_back(c);
  }
  long long f=dfs(0);
  printf("Case %d: %lld\n",t-T,sum);
 }
 return 0; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值