[USACO06FEB]稳定奶牛分配Steady Cow Assignment

题目链接

稳定奶牛分配

题目描述

农夫约翰有(N(1 le N le 1000))只奶牛,每只奶牛住在(B(1 le B le 20))个奶牛棚中的一个。当然,奶牛棚的容量有限。有些奶牛对它现在住的奶牛棚很满意,有些就不太满意了。
农夫约翰想要重新安排这些奶牛,使得奶牛的满意度尽可能相同,尽管有可能这意味者所有的奶牛都不喜欢新分配的奶牛棚。
每只奶牛都按顺序给出她喜欢的奶牛棚。在某个分配方案中,一只奶牛的满意度等于她对她的奶牛棚的评价等级。你的工作是找出一种分配方案使得没有奶牛棚超出它的容量,而且奶牛给分配到的奶牛棚的评价等级的相对范围(即分配到的等级最高的奶牛棚和等级最低的奶牛棚之间的差值)尽可能的小。

输入格式

第一行:两个用空格隔开的整数,(N)和B
第二行到第(N+1)行:每一行都有(B)个用空格隔开的正整数,它们恰好是(1)(B)的一个排列。第(i+1)行的第一个整数是第(i)只奶牛的首选牛棚的编号,该行的第二个整数是第(i)只奶牛的第二选择,等等。
(N+2)行:(B)个用空格隔开的整数,分别表示这(B)个奶牛棚的容量。这些数的和保证至少为(N)

输出格式

一个整数,被分配到的牛棚等级的最小相对差值。

样例输入

6 4
1 2 3 4
2 3 1 4
4 2 3 1
3 1 2 4
1 3 4 2
1 4 2 3
2 1 3 2

样例输出

2

提示

每个奶牛都被安排在它的第一选择或第二选择:(1)号牛棚安排(1)号奶牛和(5)号奶牛,(2)号牛棚安排(2)号奶牛,(3)号牛棚安排(4)号奶牛,(4)号牛棚安排(3)号奶牛和(6)号奶牛。

题解

要求求出把奶牛分别分配到牛棚后最小的满意度的差值。
我们看到牛棚的个数很少,只有(20),所以我们可以暴力枚举答案,然后判断能否有安排方法满足条件。
我们考虑把(s)到每个奶牛连一条流量为(1)的边,然后把所有牛棚到(t)连一条流量为这个牛棚容量的边。
然后不可能把每个奶牛和牛棚都连边,要求所连所有边中最大的值减最小的值(满意度)要小于等于(ans)
那么我们只要再根据(ans)枚举区间就好了,然后把满意度在区间内的边全部连上,然后跑一遍网络流。
如果最大流等于牛的个数(也就是所有牛都能通过在限制范围内的边到牛棚里),那么这个方案就可行。
当然你可以从小到大枚举(ans),也可以用二分来求(ans)(反正(B)只有20,差距不大)。

但是你发现如果不加任何优化的话会有一个点跑不过去。
我们首先用当前弧优化(这个不用说都会加上吧)。
但还是跑不过去。
我们考虑(s)到奶牛的流量只有(1),如果找到增广路之后就可以直接退到(s)了,所以我们判断如果当前最大值为(0)的话直接退出,不用再往下扫了。
但是这还是过不去。
我们再考虑如果扫完一个点之后,流量还有剩余(就是当前弧优化后到这个点的最大值减去流量之后不为(0)),那么就说明(s)到这个点有流量,但是这个点到(t)已经没有流量了,那么其他点就不能通过这个点到(t)有增广路了,也就是说接下来这个点不用再扫了,那么我们把这个点的(dis[])值设为(-1)就好了。
加上这些优化就能过所有的点了。
上代码:

#include<bits/stdc++.h>
#define inf 2000000000
using namespace std;
int n,b;
int a[1009][29];
int s[29];
struct aa{
    int to,nxt,w;
}p[50000];
int h[2009],len;
void add(int u,int v,int w){
    p[++len].to=v;
    p[len].nxt=h[u];
    p[len].w=w;
    h[u]=len;
    p[++len].to=u;
    p[len].nxt=h[v];
    p[len].w=0;
    h[v]=len;
}
int t;
int uu[5009];
int l,r;
int dis[5009];
bool bfs(){
    memset(dis,-1,sizeof(dis));
    dis[0]=0;
    uu[l=r=1]=0;
    while(l<=r){
        int u=uu[l++];
        for(int j=h[u];j;j=p[j].nxt){
            if(p[j].w && dis[p[j].to]==-1){
                dis[p[j].to]=dis[u]+1;
                uu[++r]=p[j].to;
            }
        }
    }
    return dis[t]!=-1;
}
int dfs(int u,int mx){
    if(u==t) return mx;
    int oo=0,xx;
    for(int j=h[u];j;j=p[j].nxt){
        if(dis[p[j].to]==dis[u]+1){
            xx=dfs(p[j].to,min(mx,p[j].w));
            oo+=xx;
            mx-=xx;
            p[j].w-=xx;
            p[j^1].w+=xx;
            if(mx==0) break;//最大值为$0$,直接退出
        }
    }
    if(mx!=0) dis[u]=-1;//当前点不用再被扫了
    return oo;
}
int dinic(){
    int ans=0;
    while(bfs()) ans+=dfs(0,inf);
    return ans;
}
int main(){
    scanf("%d%d",&n,&b);
    t=n+b+1;
    for(int j=1;j<=n;j++)
        for(int i=1;i<=b;i++)
            scanf("%d",&a[j][i]);
    for(int j=1;j<=b;j++)
        scanf("%d",&s[j]);
    for(int l=1;l<=b;l++){
        for(int j=1;j+l-1<=b;j++){
            memset(h,0,sizeof(h));
            len=1;
            for(int i=1;i<=n;i++)
                add(0,i,1);
            for(int i=1;i<=b;i++)
                add(n+i,t,s[i]);
            for(int o=1;o<=n;o++)
                for(int oo=j;oo<=j+l-1;oo++)
                    add(o,n+a[o][oo],1);
            if(dinic()==n){
                printf("%d",l);
                return 0;
            }
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/linjiale/p/12026105.html