【题意】
给一个含有n个点,cnt条边的图(1<= n <= 100,0 <= cnt <= 200),问次小生成树是多少,如果无法生成次小生成树,输出No second way,如果最小生成树也无法生成,输出No way。
【思路】
直接Kruskal或Prim算法求一下最小生成树,然后枚举这棵生成树中的边剔除来生成次小生成树。注意Prim算法,若用链式前向星来建图,由于是双向边,所以数组还得开得更大些。
【代码1】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=105,INF=0x3f3f3f3f;
struct edge{
int from,to,cost;
bool operator<(const edge &another)const
{
return cost<another.cost;
}
};
int t,n,cnt;
int fa[MAXN];
edge e[2*MAXN];
bool used[2*MAXN];
void init()
{
for(int i=1;i<=n;i++)
fa[i]=i;
}
int findfather(int x)
{
return (x==fa[x]?x:fa[x]=findfather(fa[x]));
}
int kruskal(int x)
{
init();
if(x==0){
memset(used,false,sizeof(used));
int tot=0,ans=0;
for(int i=1;i<=cnt;i++){
int p,q;
p=findfather(e[i].from);
q=findfather(e[i].to);
if(p!=q){
fa[q]=p;
used[i]=true;
tot++;
ans+=e[i].cost;
if(tot==n-1)return ans;
}
}
return INF;
}
else{
int tot=0,ans=0;
for(int i=1;i<=cnt;i++){
if(i==x)continue;
int p,q;
p=findfather(e[i].from);
q=findfather(e[i].to);
if(p!=q){
fa[q]=p;
tot++;
ans+=e[i].cost;
if(tot==n-1)return ans;
}
}
return INF;
}
}
int main()
{
scanf("%d",&t);
for(int kase=1;kase<=t;kase++){
scanf("%d %d",&n,&cnt);
for(int i=1;i<=cnt;i++){
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
e[i].from=a;
e[i].to=b;
e[i].cost=c;
}
sort(e+1,e+1+cnt);
if(kruskal(0)==INF&&n>1){
printf("Case #%d : No way\n",kase);
continue;
}
int ans=INF;
for(int i=1;i<=cnt;i++)
if(used[i])ans=min(ans,kruskal(i));
if(ans==INF)
printf("Case #%d : No second way\n",kase);
else
printf("Case #%d : %d\n",kase,ans);
}
return 0;
}
【代码2】
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN=105,INF=0x3f3f3f3f;
struct edge{
int from,to,cost,next;
};
int t,n,m,cnt,minpoint,minlen,minnum;
int head[MAXN];
bool visited[MAXN],used[4*MAXN];
edge e[4*MAXN];
queue<int> q;
void addedge(int from,int to,int cost)
{
cnt++;
e[cnt].to=to;
e[cnt].cost=cost;
e[cnt].next=head[from];
head[from]=cnt;
}
void findone(int x)
{
int y=(x&1?x+1:x-1);
if(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i!=0;i=e[i].next){
if(i==x||i==y)continue;
int v=e[i].to;
if(!visited[v]&&minlen>e[i].cost){
minlen=e[i].cost;
minpoint=v;
minnum=i;
}
}
findone(x);
q.push(u);
}
}
int prim(int x)
{
memset(visited,false,sizeof(visited));
while(!q.empty())q.pop();
q.push(1);
visited[1]=true;
int ans=0,tot=1;
minlen=INF;
findone(x);
if(x==0){
memset(used,false,sizeof(used));
while(minlen!=INF){
int v=minpoint;
q.push(v);
visited[v]=true;
used[minnum]=true;
if(minnum&1)
used[minnum+1]=true;
else
used[minnum-1]=true;
ans+=minlen;
tot++;
if(tot==n)break;
minlen=INF;
findone(x);
v=minpoint;
}
}
else{
while(minlen!=INF){
int v=minpoint;
q.push(v);
visited[v]=true;
ans+=minlen;
tot++;
if(tot==n)break;
minlen=INF;
findone(x);
}
}
if(tot!=n)ans=INF;
return ans;
}
int main()
{
scanf("%d",&t);
for(int kase=1;kase<=t;kase++){
scanf("%d %d",&n,&m);
memset(head,0,sizeof(head));
cnt=0;
for(int i=1;i<=m;i++){
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
addedge(a,b,c);
addedge(b,a,c);
}
if(prim(0)==INF){
printf("Case #%d : No way\n",kase);
continue;
}
int ans=INF;
for(int i=1;i<=cnt-1;i+=2)
if(used[i])ans=min(ans,prim(i));
if(ans==INF)
printf("Case #%d : No second way\n",kase);
else
printf("Case #%d : %d\n",kase,ans);
}
return 0;
}