说是图论,其实并不是标准的图论题呢。

传送门:GO


一开始完全没看懂如何完成,一看题解,顿悟这道题的精髓。

首先考虑到最简单的两个点1,2,ans=dis(1,2).

如果从i,j之间衍生出一条边,连向另外一点3,则有

ans=dis(1,2)+(dis(1,3)+dis(2,3)-dis(1,2))/2

如果再从2,3中衍生出一点4呢?

ans有两种情况:

1.ans=(dis(1,4)+dis(2,4)-dis(1,2))/2

2.ans=(dis(1,4)+dis(3,4)-dis(1,3))/2

实际上我们只用求延伸出的一段,其余的部分我们在之前就会求出来,所以那一段一定是最小的那一段。

所以列出一个公式:

 (图片来源于Mathison的博客:GO

之后就可以打代码AC了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int read(){
 4     int x=0,f=1;
 5     char c=getchar();
 6     while(!isdigit(c)){
 7         if(c=='-') f=-1;
 8         c=getchar();
 9     }
10     while(isdigit(c)){
11         x=(x<<1)+(x<<3)+(c^48);
12         c=getchar();
13     }
14     return x*f;
15 }
16 int n,ans,now;
17 int dis[40][40];
18 int main(){
19     while(1){
20         n=read();
21         if(!n) break;
22         for(int i=1;i<n;i++){
23             for(int j=i+1;j<=n;j++){
24                 dis[i][j]=dis[j][i]=read();
25             }
26         }
27         ans=dis[1][2];
28         for(int i=3;i<=n;i++){
29             now=1e9;
30             for(int j=2;j<i;j++){
31                 now=min(now,(dis[1][i]-dis[1][j]+dis[i][j])>>1);
32             }
33             ans+=now;
34         }
35         printf("%d\n",ans);
36     }
37     return 0;
38 } 
01-26 03:10