题意:给定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;
}
2455:要求找出网络中从给定起点到终点的不少于t条的路径,要求每条边至多被这些路径经过一次(即t条边-disjoint path)。求满足上述条件的通过的所有边的最大值的最小值。
思路:显然是二分+最大流判断。这里犯了个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;
}