您的位置:首页 > 其它

BUAA OJ 382 中世界的Thor

2017-07-25 18:06 120 查看

BUAA OJ 382 中世界的Thor

题目描述

时间限制: 1000 ms 内存限制: 65536 kb

好久没见Thor了哈,他去了哪里呢?

他去了中世界,在给那里的城镇修铁路。

中世界铁路公司要求Thor建设这样一条铁路: 在连同所有城镇的前提下,使得铁路的总长最短。

这对于Thor自然不是难事咯——不就是求一个最小生成树嘛~

然而Thor是一个贪心的工程师,他想在这其中做一些手脚,使得既满足铁路的布局是一棵生成树,其费用又比最小生成树要大上那么一丢丢,这样他便可以从预算与实际的差价之中收取一定的私利。

然而Thor这样做还是有风险的,一旦被发现后果很严重。于是谨慎起见,他准备找到这样一个方案,在既使得布局是一颗生成树,又使得其费用是除最小生成树以外最小的费用。现在这个事情被交给了你。

输入

多组数据。

对于每一组数据,第一行有两个正整数n和m,表示有n个城镇以及m条可以铺设的铁路路线。(1≤n≤300)

接下来m行,每行有三个正整数x、y、w,表示城镇x到城镇y之间可以铺设一条长度为w的铁路。城镇编号从1开始。 (1≤c≤100)

数据保证没有重边和自环,同时保证图中有环存在。

输出

对于每一组数据,输出得到的生成树的边长之和。

输入样例

3 3
1 2 1
2 3 2
1 3 3


输出样例

4


解题思路

求次小生成树

代码

方法一

先求出最小生成树,然后去掉最小生成树上的一条边,再求这个去掉了一条边的图最小生成树,这些最小生成树中的权值之和最小者(而且顶点必须仍然是n个的)就是原图的次小生成树。O(N3)O(N3)

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
typedef struct edge{
int u,v,w;
edge(int x,int y,int ww):u(x),v(y),w(ww){}
}edge;
bool cmp(edge a,edge b) {
return a.w<b.w;
}
int a[305][305];
int uf[305];
bool use[305][305];
int find(int num) {
if(uf[num]==num)return num;
return uf[num]=find(uf[num]);
}
void connect(int x,int y) {
uf[find(y)]=x;
}
int krus(int n) {
int edgeNum=1;
memset(use,0,sizeof(use));
vector<edge>vec;
vec.reserve(100000);
for(int x=1;x<=n;x++)
for(int y=1;y<=n;y++)
if(a[x][y]&&x<y)
vec.push_back(edge(x,y,a[x][y]));
sort(vec.begin(),vec.end(),cmp);
int sum=0,cnt=0;
for(int i=1;i<=300;i++)
uf[i]=i;
for(int i=0;i<vec.size()&&cnt<n-1;i++)
if(find(vec[i].v)!=find(vec[i].u)){
sum+=vec[i].w;connect(vec[i].u,vec[i].v);++cnt;
use[vec[i].u][vec[i].v]=use[vec[i].v][vec[i].u]=true;
++edgeNum;
}
return edgeNum==n?sum:-1;
}
int main() {
int n,m,x,y,w;
while(~scanf("%d%d",&n,&m)) {
memset(a,0,sizeof(a));
for(int i=0;i<m;i++) {
scanf("%d%d%d",&x,&y,&w);
a[x][y]=w;a[y][x]=w;
}
int sum=krus(n);
vector<edge>mst;mst.reserve(305);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(use[i][j])
if(i<j)
mst.push_back(edge(i,j,a[i][j]));
int t,tst,max=99999999;
bool flag=false;
for(int i=0;i<mst.size();i++){
t=mst[i].w;
a[mst[i].u][mst[i].v]=a[mst[i].v][mst[i].u]=0;
tst=krus(n);
if(tst<max&&tst!=-1)
max=tst;
a[mst[i].u][mst[i].v]=a[mst[i].v][mst[i].u]=t;
}
printf("%d\n",max);
}
}


方法二

求出最小生成树上任意两点的最短边,任意加入一条边之后必然成环,这时去掉这两点之间的最短边就会得到一棵树,这些树中权值最小的就是次小生成树。O(n2)O(n2)

#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
typedef long long ll;

struct edge{
int head;
int tail;
int weight;
bool flag;
bool operator < (const edge& p) const{
return weight<p.weight;
}
};
struct mstNode{
int rank;
int weight;
int next;
};
struct node{
int rank;
int max;//从某个点到它的路径中的最大边的长度
node(int r=-1,int m=-1):rank(r),max(m){}
};

const int MAX=100000;
int n;
int m;
int num;
int p[MAX];
int maxWeight[301][301];
int Index[301];
mstNode mstree[602];
edge edges[MAX];

void makeSet(){
for(int i=0;i<=n;i++)
p[i]=i;
}
int findSet(int x){
if(x!=p[x])
p[x]=findSet(p[x]);
return p[x];
}

//是从下向上建树的
void addEdge(int head,int tail,int weight){
mstree[num].rank=tail;
mstree[num].weight=weight;
mstree[num].next=Index[head];
Index[head]=num++;
}

inline ll kruscal(){
int i,x, y,sum=0;
makeSet();
sort(edges,edges+m);
for(i=0;i<m;i++){
x=findSet(edges[i].head);
y=findSet(edges[i].tail);
if(x!=y){
p[x]=y;
addEdge(edges[i].head,edges[i].tail,edges[i].weight);
addEdge(edges[i].tail,edges[i].head,edges[i].weight);
edges[i].flag=true;
sum+=edges[i].weight;
}
}
return sum;
}

void bfs(int p){
int i;
bool visited[301];
memset(visited,0,sizeof(visited));
queue<node> que;
node now(p,0);
node adj;

que.push(now);
visited[p]=true;
while(!que.empty()){
node frontNode=que.front();que.pop();
for(i=Index[frontNode.rank];i!=-1;i=mstree[i].next){
adj.rank=mstree[i].rank;
adj.max=mstree[i].weight;
if(!visited[adj.rank]){
if(frontNode.max>adj.max)
adj.max=frontNode.max;
maxWeight[p][adj.rank]=adj.max;
visited[adj.rank] = true;
que.push(adj);
}
}
}
}

ll secondaryMST(){
int i;
ll sum=kruscal();
ll secSum=999999999;
ll tem;
for(i=1;i<=n;i++)
bfs(i);
for(i=0;i<m;i++)
if(!edges[i].flag){
tem=sum+edges[i].weight-maxWeight[edges[i].head][edges[i].tail];
if(secSum>tem)
secSum=tem;
}
return secSum;
}

int main(){
while(~scanf("%d %d",&n,&m)){
for(int i=0; i<m;i++){
scanf("%d %d %d",&edges[i].head,&edges[i].tail,&edges[i].weight);
edges[i].flag=false;
}
num=0;
memset(Index,-1,sizeof(Index));
printf("%lld\n",secondaryMST());
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  次小生成树