poj2112 最大流
我用Dinic写的。G++ 1800ms 很慢,c++直接超时。优化后的 141ms,很快!
对于此题,建图方法很巧妙,通常想到求距离,那就会朝距离的方向建图,但是这题根据牛个数来建图,然后二分距离。
先求出任意点之间的最短距离。对于挤奶器,牛,很明显的分为2部分。挤奶器的牛来自牛这部分。先另外设源点和汇点。对于牛部分,都与源点相连,容量为1。然后二分
距离,对于挤奶器和牛之间的容量,如果挤奶器和牛之间的距离小于或等于二分的距离,那么此路可以通过牛。然后挤奶器与汇点之间的容量为m值。这样图就建完了。然后Dinic
求最大流(此时最大流的值表示牛的个数),如果此时最大流的值>=c,即满足牛的个数,那这个距离是可以的,然后继续二分,知道得到结果。
#include<stdio.h> #include<string.h> #include<queue> #define maxn 300 #define INF 99999999 using namespace std; int map[maxn][maxn],dis[maxn][maxn],vis[maxn]; int k,c,m; int min(int x,int y) { return x<y?x:y; } void floyd(int n) { int i,j,t; for(t=1;t<=n;t++) { for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { if(dis[i][j]>dis[i][t]+dis[t][j]) dis[i][j]=dis[i][t]+dis[t][j]; } } } } void makemap(int maxval,int n) { int i,j; memset(map,0,sizeof(map)); for(i=1;i<=k;i++) map[i][n+1]=m; for(i=k+1;i<=n;i++) map[0][i]=1; for(i=k+1;i<=n;i++) { for(j=1;j<=k;j++) { if(dis[i][j]<=maxval) map[i][j]=1; } } } int BFS(int n) { int i,j; queue<int>q; memset(vis,-1,sizeof(vis)); vis[0]=0; q.push(0); while(!q.empty()) { int t=q.front(); q.pop(); for(i=0;i<=n+1;i++) { if(vis[i]<0&&map[t][i]) { q.push(i); vis[i]=vis[t]+1; } } } if(vis[n+1]>0) return 1; return 0; } int dfs(int u,int low,int n) { int i,j,a; if(u==n) return low; for(i=0;i<=n;i++) { if(vis[i]==vis[u]+1&&map[u][i]) { a=dfs(i,min(low,map[u][i]),n); if(!a)continue; map[u][i]-=a; map[i][u]+=a; return a; } } return 0; } int main() { int i,j,n; while(scanf("%d%d%d",&k,&c,&m)!=EOF) { n=k+c; for(i=1;i<=k+c;i++) { for(j=1;j<=k+c;j++) { scanf("%d",&dis[i][j]); if(dis[i][j]==0)//不连通给予无穷,防止floyd出现问题 dis[i][j]=INF; } } floyd(n); /*for(i=1;i<=k+c;i++) { for(j=1;j<=k+c;j++) { printf("%d ",dis[i][j]); } printf("\n"); }*/ int L=0,R=40000; int ans=0; int rt=0; while(L<=R)//二分答案 { rt=0; int mid=(L+R)/2; makemap(mid,n);//根据二分的值建图 while(BFS(n)) { int fa=dfs(0,INF,n+1); if(!fa) break;; rt+=fa; } if(rt>=c) { R=mid-1; ans=mid; } else { L=mid+1; } } printf("%d\n",ans); } }
优化后:
#include<stdio.h> #include<string.h> #include<queue> #define maxn 300 #define INF 99999999 using namespace std; int map[maxn][maxn],dis[maxn][maxn],vis[maxn]; int k,c,m; int min(int x,int y) { return x<y?x:y; } void floyd(int n) { int i,j,t; for(t=1;t<=n;t++) { for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { if(dis[i][j]>dis[i][t]+dis[t][j]) dis[i][j]=dis[i][t]+dis[t][j]; } } } } void makemap(int maxval,int n) { int i,j; memset(map,0,sizeof(map)); for(i=1;i<=k;i++) map[i][n+1]=m; for(i=k+1;i<=n;i++) map[0][i]=1; for(i=k+1;i<=n;i++) { for(j=1;j<=k;j++) { if(dis[i][j]<=maxval) map[i][j]=1; } } } int BFS(int n) { int i,j; queue<int>q; memset(vis,-1,sizeof(vis)); vis[0]=0; q.push(0); while(!q.empty()) { int t=q.front(); q.pop(); for(i=0;i<=n+1;i++) { if(vis[i]<0&&map[t][i]) { q.push(i); vis[i]=vis[t]+1; } } } if(vis[n+1]>0) return 1; return 0; } int dfs(int u,int low,int n) { int i,j,a,used=0; if(u==n) return low; for(i=0;i<=n&&used<low;i++) { if(vis[i]==vis[u]+1&&map[u][i]) { a=dfs(i,min(low-used,map[u][i]),n);//多路增广 if(!a)continue; map[u][i]-=a; map[i][u]+=a; used+=a; } } if(!used) vis[u]=-1; return used; } int main() { int i,j,n; while(scanf("%d%d%d",&k,&c,&m)!=EOF) { n=k+c; for(i=1;i<=k+c;i++) { for(j=1;j<=k+c;j++) { scanf("%d",&dis[i][j]); if(dis[i][j]==0)//不连通给予无穷,防止floyd出现问题 dis[i][j]=INF; } } floyd(n); /*for(i=1;i<=k+c;i++) { for(j=1;j<=k+c;j++) { printf("%d ",dis[i][j]); } printf("\n"); }*/ int L=0,R=40000; int ans=0; int rt=0; while(L<=R)//二分答案 { rt=0; int mid=(L+R)/2; makemap(mid,n);//根据二分的值建图 while(BFS(n)) { int fa=dfs(0,INF,n+1); if(!fa) break;; rt+=fa; } if(rt>=c) { R=mid-1; ans=mid; } else { L=mid+1; } } printf("%d\n",ans); } }