题目大意: n个村庄,两两有路,有一些已经建成,有一些未建成,问求使得任意两个村庄都可以连通的最小费用
思路:已经建成的路令其费用为0,求最小生成树即可.
解法一:这里用最简单的prim+邻接矩阵:果断很慢O(n^2)450ms
AC Program:
#include <algorithm> #include <iostream> #include <iomanip> #include <cstdio> #include <cmath> #include <string.h> typedef long long ll; #define clr(a) memset((a),0,sizeof (a)) #define rep(i,a,b) for(int i=(a);i<(int)(b);i++) #define per(i,a,b) for(int i=((a)-1);i>=(int)(b);i--) #define inf (0x7fffffff) #define eps 1e-6 #define MAXN #define MODN (1000000007) using namespace std; int n; int mm[110][110]; int vis[110]; int dis[110]; void prim(){ for(int i=1;i<=n;i++){ vis[i]=0; dis[i]=mm[1][i]; } dis[1]=0; vis[1]=1; int k,min; for(int i=1;i<=n;i++){ min=inf; for(int j=1;j<=n;j++){ if(!vis[j]&&min>dis[j]){ min=dis[j]; k=j; } } vis[k]=1; for(int j=1;j<=n;j++){ if(!vis[j]&&dis[j]>mm[k][j]){ dis[j]=mm[k][j]; } } } int sum=0; for(int i=1;i<=n;i++){ sum+=dis[i]; } printf("%d\n",sum); } int main(){ int u,v,w,sta; while(~scanf("%d",&n)&&n){ for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) mm[i][j]=inf; int edge=n*(n-1)/2; for(int i=1;i<=edge;i++){ scanf("%d%d%d%d",&u,&v,&w,&sta); if(sta==1){ mm[u][v]=0;//一开始忘了建立双向边,晕 mm[v][u]=0; } else{ mm[u][v]=w; mm[v][u]=w; } } prim(); }; //system("pause"); return 0; }
解法二:想玩一下邻接表,就了.prim+邻接表,想象中更慢 O(n*m) 680ms
#include <algorithm> #include <iostream> #include <iomanip> #include <cstdio> #include <cmath> #include <string.h> typedef long long ll; #define clr(a) memset((a),0,sizeof (a)) #define rep(i,a,b) for(int i=(a);i<(int)(b);i++) #define per(i,a,b) for(int i=((a)-1);i>=(int)(b);i--) #define inf (0x7fffffff) #define eps 1e-6 #define MAXN #define MODN (1000000007) using namespace std; int n; //int mm[110][110]; int head[110]; int vis[110]; int dis[110]; struct edge{ int to,w,next; }e[5500*100+5]; void prim(){ for(int i=1;i<=n;i++){ //初始化标记数组和距离数组 vis[i]=0; dis[i]=inf; } for(int i=head[1];i!=-1;i=e[i].next){//寻找1可以到的点初始化 int ti=e[i].to;//一开始没有这句话,囧..这是因为to是要用来初始化,不是边的编号用来初始化 dis[ti]=e[i].w; //cout<<"i "<<i<<endl; } dis[1]=0; vis[1]=1; /*/----- for(int i=1;i<=n;i++){ cout<<"dis[i] "<<dis[i]<<" "<<endl; } /*/ int k,min; for(int i=1;i<=n;i++){ min=inf; for(int j=1;j<=n;j++){ if(!vis[j]&&min>dis[j]){ //必须扫描一遍找出最短的 min=dis[j]; k=j; } } vis[k]=1; for(int j=head[k];j!=-1;j=e[j].next){//找出当前点可以到的边进行更新 int tj=e[j].to; if(!vis[tj]&&dis[tj]>e[j].w) dis[tj]=e[j].w; } /* for(int j=1;j<=n;j++){ if(!vis[j]&&dis[j]>mm[k][j]){ dis[j]=mm[k][j]; } } */ } int sum=0; for(int i=1;i<=n;i++){ sum+=dis[i]; } printf("%d\n",sum); } int main(){ int u,v,wei,sta; while(~scanf("%d",&n)&&n){ int edge=n*(n-1)/2; int cnt=0; memset(head,-1,sizeof(head)); memset(e,0,sizeof(e)); for(int i=1;i<=edge;i++){ scanf("%d%d%d%d",&u,&v,&wei,&sta); if(sta==1){ //初始化,建立邻接表 e[cnt].to=v; e[cnt].w=0; e[cnt].next=head[u]; head[u]=cnt++; e[cnt].to=u; e[cnt].w=0; e[cnt].next=head[v]; head[v]=cnt++; } else{ e[cnt].to=v; e[cnt].w=wei; e[cnt].next=head[u]; head[u]=cnt++; e[cnt].to=u; e[cnt].w=wei; e[cnt].next=head[v]; head[v]=cnt++; } } prim(); }; //system("pause"); return 0; }