题意:给定n头牛,m个牛棚,每个牛都有对m个牛棚的一个排序,表示自己住在其中的舒适程度。通过n*m矩阵给出,每一行是一个1...m的排列。然后给出每个牛棚的容量,即最大装载牛的数量。现在要找出一个牛棚居住的安排,让每头牛都有牛棚呆,并且让舒适度最小的和最大的差值最小(即尽量让所有牛的舒服程度差不多)。
思路:这种套路的题做的比较多了,就是二分答案+最大流构图判断。这题同样是二分最小差值,然后对于当前差值枚举可能的情况,建图dinic判断。
需要注意的是dinic里当a==0时需要break是一个重要的剪枝。
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <cstdlib> using namespace std; #define clc(s,t) memset(s,t,sizeof(s)) #define INF 0x3fffffff #define N 1005 int n,m,s[N][22],w[22]; struct edge{ int y,next,c; }e[2*(N*20+N+20)]; int first[N+30],top,f[N+30]; int flag[N+30]; void add(int x,int y,int c){ e[top].y = y; e[top].c = c; e[top].next = first[x]; first[x] = top++; } void create(int a,int b){ int i,j; clc(first, -1); top = 0; for(i = 1;i<=n;i++) add(0, i, 1),add(i, 0, 0); for(i = 1;i<=m;i++) add(n+i,n+m+1,w[i]),add(n+m+1,n+i,0); for(i = 1;i<=n;i++) for(j = a;j<=b;j++) add(i,n+s[i][j],1),add(n+s[i][j],i,0); } int bfs(int s,int t){ int i,now; queue<int> q; clc(flag, -1); flag[s] = 0; q.push(s); while(!q.empty()){ now = q.front(); q.pop(); for(i = first[now];i!=-1;i=e[i].next){ if(flag[e[i].y] == -1 && e[i].c>0){ flag[e[i].y] = flag[now]+1; if(e[i].y == t) return 1; q.push(e[i].y); } } } return 0; } int dfs(int s,int t,int a){ int j,res = 0; if(s==t || !a) return a; for(int i = f[s];i!=-1;i=f[s]=e[i].next){ if(e[i].c>0 && flag[e[i].y]==flag[s]+1 && (j = dfs(e[i].y,t,min(a,e[i].c)))){ res += j; e[i].c -= j; e[i^1].c += j; a -= j; if(!a)//这个剪枝是如此重要以至于不加它就会TLE break; } } return res; } int dinic(int s,int t){ int res=0; while(bfs(s,t)){ memcpy(f, first, sizeof(f)); res += dfs(s,t,INF); } return res; } int main(){ int i,j,low,high,mid; scanf("%d %d",&n,&m); for(i = 1;i<=n;i++) for(j = 1;j<=m;j++) scanf("%d",&s[i][j]); for(j = 1;j<=m;j++) scanf("%d",&w[j]); low = 1; high = m; while(low < high){ mid = (low+high)>>1; for(i = 1;i<=m+1-mid;i++){ create(i,i+mid-1); if(n == dinic(0,n+m+1)) break; } if(i<=m+1-mid) high = mid; else low = mid+1; } printf("%d\n",high); return 0; }
思路:显然是二分+最大流判断。这里犯了个2,重边只计算了一次,属于读题不仔细。题目说每条边最多用一次,而不是两点之间最多通过一次。所以如果有k重边,那么最多可以通过k次,而不是1次。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> #include <queue> using namespace std; #define INF 0x3fffffff #define clr(s,t) memset(s,t,sizeof(s)) #define N 205 int n,m,k; int first[N],f[N],top,visited[N]; struct edge{ int y,next,c; }e[40005*4]; struct tmp{ int x,y,w; }a[40005]; void add(int x,int y,int c){ e[top].y = y; e[top].c = c; e[top].next = first[x]; first[x] = top++; } void create(int x){ int i; clr(first, -1); top = 0; for(i = 0;i<m;i++) if(a[i].w <= x){ add(a[i].x,a[i].y,1);add(a[i].y,a[i].x,0); add(a[i].y,a[i].x,1);add(a[i].x,a[i].y,0); } } int bfs(int s,int t){ int i,now; queue<int> q; clr(visited, -1); visited[s] = 0; q.push(s); while(!q.empty()){ now = q.front(); q.pop(); for(i = first[now];i!=-1;i=e[i].next) if(visited[e[i].y]==-1 && e[i].c>0){ visited[e[i].y] = visited[now]+1; if(e[i].y == t) return 1; q.push(e[i].y); } } return 0; } int dfs(int s,int t,int a){ int i,j,res=0; if(s==t || !a) return a; for(i = f[s];i!=-1;i=f[s]=e[i].next) if(visited[e[i].y]==visited[s]+1 && (j=dfs(e[i].y, t, min(a,e[i].c)))){ res+=j; e[i].c -= j; e[i^1].c += j; a -= j; if(!a) break; } return res; } int dinic(int s,int t){ int res=0; while(bfs(s,t)){ memcpy(f,first,sizeof(f)); res += dfs(s,t,INF); } return res; } int main(){ int i,low,high,mid; low = INF; high = 0; scanf("%d %d %d",&n,&m,&k); for(i = 0;i<m;i++) { scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].w); low = min(low,a[i].w); high = max(high,a[i].w); } while(low < high){ mid = (low+high)>>1; create(mid); if(dinic(1,n)>=k) high = mid; else low = mid+1; } printf("%d\n",low); return 0; }