挑战程序设计竞赛 2.5 它们其实都是“图”

【Summarize】

  1.注意对图是否连通的判定

  2.灵活运用边权取负的技巧

AOJ 0189:Convenient Location

/*
    给出一张无向图,现在求一个点,使得这个点到所有点的距离和最小
    输出这个点的编号和距离和
*/
#include <cstdio>
#include <algorithm>
#include <cstring> 
using namespace std;
const int N=10,INF=0x3f3f3f3f;
int n,d[N][N],c,x,y,m;
int main(){
    while(~scanf("%d",&m),m){
        memset(d,INF,sizeof(d)); n=0;
        while(m--){
            scanf("%d%d%d",&x,&y,&c);
            d[x][y]=d[y][x]=c;
            n=max(n,max(x,y));
        }
        for(int k=0;k<=n;k++)
            for(int i=0;i<=n;i++)
                for(int j=0;j<=n;j++)d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
        int ans=INF,pos=0;
        for(int i=0;i<=n;i++){
            int tmp=0;
            for(int j=0;j<=n;j++)if(i!=j)tmp+=d[i][j];
            if(tmp<ans)ans=tmp,pos=i;
        }printf("%d %d
",pos,ans);
    }return 0;
}

POJ 2139:Six Degrees of Cowvin Bacon

/*
    求一头奶牛到其它奶牛的平均距离的100倍的最小值
*/
#include <cstdio>
#include <algorithm>
#include <cstring> 
using namespace std;
const int N=310,INF=0x3f3f3f3f;
int n,d[N][N],c,x,y,m,a[N];
int main(){
    while(~scanf("%d%d",&n,&m)){
        memset(d,INF,sizeof(d)); 
        while(m--){
            scanf("%d",&c);
            for(int i=1;i<=c;i++)scanf("%d",&a[i]),a[i]--;
            for(int i=1;i<=c;i++)for(int j=1;j<i;j++)d[a[i]][a[j]]=d[a[j]][a[i]]=1;
        }
        for(int k=0;k<n;k++)
            for(int i=0;i<n;i++)
                for(int j=0;j<n;j++)d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
        int ans=INF;
        for(int i=0;i<n;i++){
            int tmp=0;
            for(int j=0;j<n;j++)if(i!=j)tmp+=d[i][j];
            if(tmp<ans)ans=tmp;
        }printf("%d
",100*ans/(n-1));
    }return 0;
}

POJ 3268:Silver Cow Party

/*
    在有向图中求每个点到终点后返回的最短路的最大值
    以终点为源点做一次正向路径的spfa和一次反向路径的spfa,
    相加即为每个点的答案
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=200010,inf=~0U>>2,M=200000;
int T,tim[N],q[N],size,h,t,n,m,x,ed[2],dis[2][N],in[N],nxt[2][N],w[2][N],v[2][N],g[2][N],u,e,cost;
void add(int u,int x,int y,int z){v[u][++ed[u]]=y;w[u][ed[u]]=z;nxt[u][ed[u]]=g[u][x];g[u][x]=ed[u];}
int spfa(int u,int S){
    for(int i=1;i<=n;i++)dis[u][i]=inf,in[i]=0,tim[i]=0;
    tim[S]=1,dis[u][S]=0,in[S]=1;
    int i,x,size; q[h=t=size=1]=S;
    while(size){
        for(i=g[u][x=q[h]],h=(h+1)%M,size--;i;i=nxt[u][i])if(dis[u][x]+w[u][i]<dis[u][v[u][i]]){
            dis[u][v[u][i]]=dis[u][x]+w[u][i];
            if(!in[v[u][i]]){
                tim[v[u][i]]++,t=(t+1)%M,size++,in[q[t]=v[u][i]]=1;
                if(tim[v[u][i]]>n)return -1;
            }
        }in[x]=0;
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&x);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&u,&e,&cost);
        add(0,u,e,cost);
        add(1,e,u,cost);    
    }spfa(1,x);spfa(0,x);
    int ans=0;
    for(int i=1;i<=n;i++)ans=max(dis[0][i]+dis[1][i],ans);
    return printf("%d
",ans),0;
}

POJ 3259:Wormholes

/*
    给出一张n个点m条边的无向图,有w个虫洞,可以在穿越的过程中减少时间
    问有没有可能回到过去
    回到过去即存在负权回路,SPFA求解即可
    需要注意的是不是连通图,因此在做spfa的时候要将所有的点先入队一次
*/
#include <cstdio>
#include <cstring>
using namespace std;
const int N=200010,inf=~0U>>2,M=200000;
int x,S,T,time[N],q[N],size,h,t,n,m,W,ed,dis[N],in[N],nxt[N],w[N],v[N],g[N],u,e,cost;
void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;}
int spfa(int S){
    for(int i=1;i<=n;i++)dis[i]=inf,in[i]=0,time[i]=0;
    time[S]=1,dis[S]=0,in[S]=1;
    int i,x,size; q[h=t=size=1]=S;
    for(int i=1;i<=n;i++){time[i]++,t=(t+1)%M,size++,in[q[t]=i]=1;}
    while(size){
        for(i=g[x=q[h]],h=(h+1)%M,size--;i;i=nxt[i])if(dis[x]+w[i]<dis[v[i]]){
            dis[v[i]]=dis[x]+w[i];
            if(!in[v[i]]){
                time[v[i]]++,t=(t+1)%M,size++,in[q[t]=v[i]]=1;
                if(time[v[i]]>n)return -1;
            }
        }in[x]=0;
    }return dis[T];
}int cas;
int main(){
    scanf("%d",&cas);
    while(cas--){
        scanf("%d%d%d",&n,&m,&W);
        memset(v,0,sizeof(v)); memset(nxt,0,sizeof(nxt));
        memset(w,0,sizeof(w)); memset(g,0,sizeof(g)); ed=0;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&u,&e,&cost);
            add(u,e,cost);
            add(e,u,cost);
        }
        for(int i=1;i<=W;i++){
            scanf("%d%d%d",&u,&e,&cost);
            add(u,e,-cost);
        }
        if(spfa(1)==-1)puts("YES"); 
        else puts("NO");
    }return 0;
}

AOJ 2249:Road Construction

/*
    建造每条道路需要的费用已经给出,
    现在求在起点到每个点都是最短路的情况下的最小修路费用
    考虑到最后一定是树形的,因此只要保留每个点与其父节点之间的边的费用最小值即可
    在计算最短路的同时不断更新费用
*/
#include <cstdio>  
#include <cstring> 
#include <queue> 
#include <utility> 
using namespace std;  
const int N=2000100;  
const int INF=~0U>>2;  
typedef pair<int,int>seg;  
priority_queue<seg,vector<seg>,greater<seg> >q;     
int d[N],D[N],head[N],u[N],v[N],w[N],nxt[N],cost[N],n,m,a,b,c,cc,ed=0,H,x[N],y[N]; 
bool vis[N];  
void add(int a,int b,int c,int cos){  
    u[++ed]=a,v[ed]=b,w[ed]=c;cost[ed]=cos;
    nxt[ed]=head[u[ed]]; head[u[ed]]=ed;     
}     
void Dijkstra(int src){  
    memset(vis,0,sizeof(vis));  
    for(int i=1;i<=n;i++)d[i]=INF;  
    d[src]=0;  
    q.push(make_pair(d[src],src));  
    while(!q.empty()){  
        seg now=q.top(); q.pop();  
        int x=now.second;  
        if(d[x]<now.first)continue; 
        for(int e=head[x];e!=-1;e=nxt[e]) 
        if(d[v[e]]>d[x]+w[e]){ 
            d[v[e]]=d[x]+w[e];  
            D[v[e]]=cost[e];
            q.push(make_pair(d[v[e]],v[e]));  
        }else if(d[v[e]]==d[x]+w[e]){
            D[v[e]]=min(cost[e],D[v[e]]);
        }
    }
} 
int main(){
	  while(~scanf("%d%d",&n,&m),m+n){
		    memset(head,-1,sizeof(head));ed=0;
		    memset(nxt,-1,sizeof(nxt));
	      while(m--){
	    	    scanf("%d%d%d%d",&a,&b,&c,&cc);
	    	    add(a,b,c,cc);add(b,a,c,cc);
	      }Dijkstra(1);
	      int ans=0;
	      for(int i=2;i<=n;i++)ans+=D[i];
		    printf("%d
",ans);
	  }return 0;
}

AOJ 2200:Mr. Rito Post Office

/*
    一张图中有陆路和水路,水路必须要有船才能走,当船开到x点时就会停在x点
    一开始人和船都在1点,问按给出顺序访问一些点需要的最短时间
    利用floyd可以得出只走陆路和只走水路时两点间的最短路
    dp[i][j]表示走到了第i个需要访问的村庄,船停在j点的最短路,然后顺序dp更新状态即可
*/
#include <cstdio>
#include <algorithm>
#include <cstring> 
#define rep(i,n) for(int i=1;i<=n;i++)
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
int n,m,q,x,y,z,d[1010]; char c;
int dl[210][210],ds[210][210],dp[1010][210];
int main(){
    while(~scanf("%d%d",&n,&m),n+m){
        rep(i,n)rep(j,n)ds[i][j]=dl[i][j]=i==j?0:INF;
        rep(i,m){
            scanf("%d%d%d %c",&x,&y,&z,&c);
            if(c=='S')ds[x][y]=ds[y][x]=min(ds[x][y],z);
            else dl[x][y]=dl[y][x]=min(dl[x][y],z);
        }scanf("%d",&q);
        rep(i,q)scanf("%d",d+i);
        rep(k,n)rep(i,n)rep(j,n){
            ds[i][j]=min(ds[i][j],ds[i][k]+ds[k][j]);
            dl[i][j]=min(dl[i][j],dl[i][k]+dl[k][j]);
        }memset(dp,INF,sizeof(dp));
        dp[1][d[1]]=0;
        rep(i,q)rep(j,n){
            dp[i][j]=min(dp[i][j],dp[i-1][j]+dl[d[i-1]][d[i]]);
            rep(k,n)dp[i][k]=
                min((LL)dp[i][k],(LL)dp[i-1][j]+dl[d[i-1]][j]+ds[j][k]+dl[k][d[i]]);  
        }printf("%d
",*min_element(dp[q],dp[q]+n+1));
    }return 0;
}

POJ 1258:Agri-Net

/*
    裸最小生成树
*/
#include <cstdio>
#include <algorithm>
using namespace std;
struct data{int x,y,c;}p[20010];  
int n,m,f[110],x;
bool cmp(data a,data b){return a.c<b.c;}
int sf(int x){return x==f[x]?x:f[x]=sf(f[x]);}
int main(){  
    while(~scanf("%d",&n)){
    	  m=0;
        for(int i=1;i<=n;i++)f[i]=i; 
        for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){
            scanf("%d",&x);
            if(i<j){p[++m].c=x;p[m].x=i;p[m].y=j;}
        }sort(p+1,p+m+1,cmp);
        int ans=0;
        for(int i=1;i<=m;i++){
            if(sf(p[i].x)!=sf(p[i].y)){
                ans+=p[i].c;
                f[sf(p[i].x)]=sf(p[i].y);
            }
        }printf("%d
",ans);
	  }return 0;  
}  

POJ 2377:Bad Cowtractors

/*
    裸最大生成树,需要注意判断无解的情况
*/
#include <cstdio>
#include <algorithm>
using namespace std;
struct data{int x,y,c;}p[20010];  
int n,m,f[1010],x;
bool cmp(data a,data b){return a.c>b.c;}
int sf(int x){return x==f[x]?x:f[x]=sf(f[x]);}
int main(){
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=n;i++)f[i]=i;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].c);
        }sort(p+1,p+m+1,cmp);
        int ans=0,cnt=1;
        for(int i=1;i<=m;i++){
            if(sf(p[i].x)!=sf(p[i].y)){
                ans+=p[i].c;
                cnt++;
                f[sf(p[i].x)]=sf(p[i].y);
            }
        }if(cnt!=n)puts("-1");
        else printf("%d
",ans);
    }return 0;
}

AOJ 2224:Save your cats

/*
    删除一些边使得图中没有环,求删除边的最小代价
*/
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=100100;
int n,m,a,b,x[N],y[N],f[N];
struct data{int x,y;double c;}p[N];
bool cmp(data a,data b){return a.c<b.c;}
int sf(int x){return f[x]==x?x:f[x]=sf(f[x]);}
int sqr(int x){return x*x;}
int main(){
    while(~scanf("%d%d",&n,&m)){
        double sum=0;
        for(int i=1;i<=n;i++)scanf("%d%d",&x[i],&y[i]),f[i]=i;
        for(int i=1;i<=m;i++){
            scanf("%d%d",&a,&b);
            double c=sqrt(sqr(x[a]-x[b])+sqr(y[a]-y[b]));
            sum+=c;
            p[i].x=a;p[i].y=b;
            p[i].c=-c;
        }sort(p+1,p+m+1,cmp);
        for(int i=1;i<=m;i++){
            if(sf(p[i].x)!=sf(p[i].y)){
                sum+=p[i].c;
                f[sf(p[i].x)]=sf(p[i].y);
            }
        }printf("%.3f
",sum);
    }return 0;
}

POJ 2395:Out of Hay

/*
    求最小生成树中的最大边权
*/
#include <cstdio>
#include <algorithm>
using namespace std;
struct data{int x,y,c;}p[20010];  
int n,m,f[2010],x;
bool cmp(data a,data b){return a.c<b.c;}
int sf(int x){return x==f[x]?x:f[x]=sf(f[x]);}
int main(){
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=n;i++)f[i]=i;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].c);
        }sort(p+1,p+m+1,cmp);
        int ans=0;
        for(int i=1;i<=m;i++){
            if(sf(p[i].x)!=sf(p[i].y)){
                ans=max(ans,p[i].c);
                f[sf(p[i].x)]=sf(p[i].y);
            }
        }printf("%d
",ans);
    }return 0;
}
原文地址:https://www.cnblogs.com/forever97/p/6092200.html