poj 3189/2455 dinic(最相近的舒服程度)

题意:给定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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值