畅通工程
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 32680 Accepted Submission(s): 14407
行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。
3 3 1 2 1 1 3 2 2 3 4 1 3 2 3 2 0 100
3 ?
(u,v)的权c[u][v]最小,那么一定存在G的一棵最小生成树,(u,v)为其中一条边。
两种求解方式及其复杂度: Prim算法,邻接矩阵时间复杂度O(n^2),邻接表时间复杂度O(mlog2n);Kruskal算法时间复杂度O(mlogm)。n为点的个数,m为边的条数。
Prim算法
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
具体实现:以任意点为顶点,找到此点所有可以到达的点,标记当前顶点,找到可到达的点最近的顶点标记,将此顶点可到达的顶点加入数组,用来再去找可以路径最短的,直到标记所有的点为止,最后输出sum。
代码模板:(邻接矩阵)
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<cstdio>
#include<set>
#define ll long long
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
const double PI=acos(-1);
const int inf=0x3f3f3f3f;
const double esp=1e-6;
const int maxn=105;
const int mod=1e9+7;
int dir[4][2]={0,1,1,0,0,-1,-1,0};
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}
ll fpow(ll n,ll k){ll r=1;for(;k;k>>=1){if(k&1)r=r*n%mod;n=n*n%mod;}return r;}
int map[maxn][maxn],visit[maxn],dis[maxn],n,m; //n代表边数,m代表点数,map存路径,dis存当前点到下一点距离,visit标记
void init()
{
int i;
mset(visit,0);
mset(map,inf);
for(i=0;i<=maxn;i++)
map[i][i]=0;
}
void prim(int s)
{
int i,j,k;
for(i=1;i<=m;i++) //赋初值
dis[i]=map[s][i];
visit[s]=1;
for(i=2;i<=m;i++)
{
int minn=inf;
k=0;
for(j=1;j<=m;j++)
{
if(!visit[j]&&minn>dis[j])
{
minn=dis[j];
k=j;
}
}
visit[k]=1;
for(j=1;j<=m;j++) //更新
{
if(!visit[j]&&dis[j]>map[k][j])
dis[j]=map[k][j];
}
}
}
int main()
{
int i,j,k,x,y,z;
while(~scanf("%d%d",&n,&m))
{
if(n==0)
break;
init();
for(i=0;i<n;i++)
{
cin>>x>>y>>z;
if(map[x][y]>z)
{
map[x][y]=map[y][x]=z;
}
}
prim(1);
int flag=1;
for(i=1;i<=m;i++)
if(dis[i]==inf)
flag=0;
if(flag)
{
int ans=0;
for(i=1;i<=m;i++)
ans+=dis[i];
printf("%d\n",ans);
}
else
cout<<"?"<<endl;
}
return 0;
}
邻接表
#include <iostream>
#include <queue>
#include <algorithm>
#include <string.h>
#include <math.h>
using namespace std;
const int maxx=200;
const int maxn=1e4+10;
const int inf=0x3f3f3f3f;
int sum,ans,m,n,q;
struct point {
int next;
int to;
int val;
bool operator < (const point &a) const
{
return val>a.val;
}
};
point pt[2*maxn];
bool vis[maxx];
int dis[maxx],head[2*maxn];
void add(int u,int v,int val)
{
pt[q].next=head[u];
pt[q].val=val;
pt[q].to=v;
head[u]=q++;
}
void prim(int st)
{
priority_queue<point>q;
point t1,t2;
ans=1,sum=0;
memset(vis,false,sizeof(vis));
memset(dis,inf,sizeof(dis));
for (int i=head[st];i!=-1;i=pt[i].next)
{
int v=pt[i].to;
if (pt[i].val<dis[v])
{
dis[v]=pt[i].val;
t1.to=v;
t1.val=dis[v];
q.push(t1);
}
}
vis[st]=true;
while (!q.empty())
{
t1=q.top();
q.pop();
int v=t1.to;
if (vis[v])continue;
vis[v]=true;
sum+=dis[v];
ans++;
for (int i=head[v];i!=-1;i=pt[i].next)
{
int u=pt[i].to;
if (!vis[pt[i].to]&&pt[i].val<dis[u])
{
dis[u]=pt[i].val;
t2.to=pt[i].to;
t2.val=dis[u];
q.push(t2);
}
}
}
}
int main()
{
int u,v,val,st;
while(~scanf("%d%d",&n,&m))
{
if (n==0)
break;
q=1;
memset(head,-1,sizeof(head));
for (int i=0;i<n;i++)
{
scanf("%d%d%d",&u,&v,&val);
add(u,v,val);
add(v,u,val);
st=u;
}
prim(st);
if (ans==m)
printf("%d\n",sum);
else
printf("?\n");
}
return 0;
}
Kruscal算法
1).记Graph中有v个顶点,e个边
2).新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边
3).将原图Graph中所有e个边按权值从小到大排序
4).循环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中
if 这条边连接的两个节点于图Graphnew中不在同一个连通分量中
添加这条边到图Graphnew中
具体实现:将边的权值经行排序,从最小的边开始添加,每次都加入最小的边,运用并查集,最后得到结果。代码模板:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<cstdio>
#include<set>
#define ll long long
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
const double PI=acos(-1);
const int inf=0x3f3f3f3f;
const double esp=1e-6;
const int maxn=105;
const int mod=1e9+7;
int dir[4][2]={0,1,1,0,0,-1,-1,0};
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}
ll fpow(ll n,ll k){ll r=1;for(;k;k>>=1){if(k&1)r=r*n%mod;n=n*n%mod;}return r;}
struct edge{
int s,t;
int len;
}p[maxn*maxn];
int pre[1005];
int n,m;
int cmp(edge a,edge b)
{
return a.len<b.len;
}
int find(int x)
{
if(pre[x]==x)
return x;
return pre[x]=find(pre[x]);
}
int main()
{
int i,j,k,x,y,z;
while(~scanf("%d%d",&n,&m)&&n)
{
for(i=1;i<=m;i++)
pre[i]=i;
for(i=0;i<n;i++)
{
cin>>x>>y>>z;
p[i].s=x;
p[i].t=y;
p[i].len=z;
}
sort(p,p+n,cmp);
int ans=0,count=0;
for(i=0;i<n&&count<m-1;i++)
{
int fx=find(p[i].s);
int fy=find(p[i].t);
if(fx!=fy)
{
pre[fx]=fy;
ans+=p[i].len;
count++;
}
}
if(count==m-1)
cout<<ans<<endl;
else
cout<<"?"<<endl;
}
return 0;
}