ACM Contest and Blackout
题目:
In order to prepare the “The First National ACM School Contest”(in 20??) the major of the city decided to provide all the schools with a reliable source of power. (The major is really afraid of blackoutsJ). So, in order to do that, power station “Future” and one school (doesn’t matter which one) must be connected; in addition, some schools must be connected as well.
You may assume that a school has a reliable source of power if it’s connected directly to “Future”, or to any other school that has a reliable source of power. You are given the cost of connection between some schools. The major has decided to pick out two the cheapest connection plans – the cost of the connection is equal to the sum of the connections between the schools. Your task is to help the major – find the cost of the two cheapest connection plans.
Input
The Input starts with the number of test cases, T (1<=T<=15) on a line. Then T test cases follow. The first line of every test case contains two numbers, which are separated by a space, N (3<=N<=100) the number of schools in the city, and M the number of possible connections among them. Next M lines contain three numbers Ai, Bi, Ci , where Ci is the cost of the connection (1<=Ci<=300) between schools Ai and Bi. The schools are numbered with integers in the range 1 to N.
Output
For every test case print only one line of output. This line should contain two numbers separated by a single space - the cost of two the cheapest connection plans. Let S1 be the cheapest cost and S2 the next cheapest cost. It’s important, that S1=S2 if and only if there are two cheapest plans, otherwise S1<=S2. You can assume that it is always possible to find the costs S1 and S2..
题意:
感觉这是一个很裸的求次小生成树的题目了,意思就是给你一个联通图,让你求出最小生成树的值和次小生成树的值。
- 首先求最小生成树用prim算法即可。
- 然后考虑如何求次小生成树,次小生成树是最小生成树换一条边得到的,所以枚举所有的不在最小生成树上的边,找到分别包括他们求出的最小生成树中最小的情况就是次小生成树。
- 求法是加入一条树外的边会形成环,为求次小生成树,在环上删除的一定是权值最大的边。就是树上从i到j路径上最大一条边。
- 利用树形dp的思想,设dp[i][j]表示从点i到点j路径上的最大边的值,在prim算法时每次找到最小边时,遍历所有vis[i]==true的点,dp[i][index]=max(dp[i][pre[index]],minn),index表示每次找到的最小边连接的点,minn表示最小边长,pre[index]表示最小边除了index以外的另外一个点。(说的有点绕,就是找该点到其他点的最长边)。
代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <cmath>
#define N 100
#define inf 0x3f3f3f
using namespace std;
int maps[N+20][N+20];
int val[N+20];
bool vis[N+20];
bool tree[N+20][N+20];
int dp[N+20][N+20];
int pre[N+20];
int n,m;
int sum=0;
int ans=0;
void prim()
{
for(int i=1;i<=n;i++)
val[i]=maps[1][i],pre[i]=1;
vis[1]=true;
pre[1]=-1;
int T=n-1;
while(T--)
{
int minn=inf;
int index=-1;
for(int i=1;i<=n;i++)
{
if(vis[i]==false&&val[i]<minn)
index=i,minn=val[i];
}
sum+=minn;
vis[index]=true;
tree[index][pre[index]]=tree[pre[index]][index]=true;
dp[index][pre[index]]=dp[pre[index]][index]=minn;
for(int i=1;i<=n;i++)
{
if(vis[i]==true&&i!=index)
dp[i][index]=dp[index][i]=max(dp[i][pre[index]],dp[index][pre[index]]);
if(vis[i]==false&&val[i]>maps[index][i])
pre[i]=index,val[i]=maps[index][i];
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
sum=0;
memset(pre,0,sizeof(pre));
memset(tree,0,sizeof(tree));
memset(vis,0,sizeof(vis));
memset(maps,inf,sizeof(maps));
memset(val,0,sizeof(val));
scanf("%d%d",&n,&m);
memset(dp,0,sizeof(dp));
int x,y,z;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
maps[x][y]=z;
maps[y][x]=z;
}
prim();
ans=inf;
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(tree[i][j]==false&&maps[i][j]!=inf)
{
ans=min(ans,sum+maps[i][j]-dp[i][j]);
}
}
}
printf("%d %d\n",sum,ans);
}
return 0;
}
P.S.感觉把一个很简单的东西讲复杂了,下次写时好好组织一下语言。