牛棚安排

Posted nibabadeboke

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛棚安排相关的知识,希望对你有一定的参考价值。

牛棚安排

(Description)

Farmer John的N(1<=N<=1000)头奶牛分别居住在农场所拥有的B(1<=B<=20)个牛棚的某一个里。有些奶牛很喜欢她们当前住的牛棚,而另一些则讨厌再在它们现在所在的牛棚呆下去。
FJ在忍受了若干次奶牛的抱怨后,决定为所有奶牛重新安排牛棚,使最不满的那头奶牛与最高兴的奶牛的心情差异最小,即使这会让所有奶牛都更加郁闷。
每头奶牛都把她对各个牛棚的好感度从高到低排序后告诉了FJ。当然,如果一头奶牛被安排到的牛棚在她给出的列表中越靠后,她就会越郁闷。你可以认为奶牛的郁闷指数是她被分配到的牛棚在列表中的位置。奶牛们是斤斤计较的,她们无法容忍别的奶牛在自己喜欢的牛棚里快乐地生活,而自己却呆在一个自己不喜欢的牛棚里。每个牛棚都只能容纳一定数量的奶牛。FJ希望在每个牛棚都没有超出容量限制的前提下,使最郁闷和最高兴的奶牛的郁闷指数的跨度最小。
FJ请你帮他写个程序,来计算这个最小的郁闷指数跨度到底是多少。

(Input)

第1行: 包含2个用空格隔开的整数N和B,分别表示牛和牛棚的数量
第2..N+1行: 每行包含B个用空格隔开的整数,刚好完全包含1..B的整数。第i+1行的第一个整数,表示奶牛i最喜欢的牛棚编号。第二个整数表示奶牛i的列表中排在第二位,也就是她第二喜欢的牛棚。依此类推。
第N+2行: 包含B个用空格隔开的整数,第i个整数表示牛棚i最多能容纳的奶牛的数目。所有牛棚能容纳奶牛头数的和至少是N。

(Output)

第1行: 输出一个整数,表示所有奶牛中最高兴与最郁闷的牛的郁闷指数跨度

(Sample Input)

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

(Sample Output)

2

思路

我刚看到这题时,没有头绪,只能打表

比赛后,看了题解,秒懂!!!

最大流+二分

解析

题目要求使最郁闷和最高兴的奶牛的郁闷指数的跨度最小,我们不妨暴力枚举最高兴的,二分指数的跨度
做完这些,可以考虑网络流。
源点: (N) 头牛,建一个超级源点连向每一个源点,流量为 (1) .
汇点: (B) 个牛棚,建一个超级汇点连向每一个汇点,流量为 牛棚能装下的牛的个数 .
再把 (N) 头牛连向在最郁闷和最高兴的范围(枚举的)中可以去的牛棚
最后跑一遍最大流就可以 (AC)

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int tot=1,n,m,a[1005][1005],b[10005],d[10005],q[10005],h[100005],cur[100005];

struct edge{
    int to,nxt,z;
}e[500000];
void add(int x,int y,int z)
{
    e[++tot].to=y;
    e[tot].nxt=h[x];
    e[tot].z=z;
    h[x]=tot;
}
void build(int x,int y)
{
    tot=1;
    memset(h,0,sizeof(h));
    for (int i=1;i<=n;i++) add(0,i,1),add(i,0,0);
    for (int i=1;i<=n;i++)
        for (int j=x;j<=y;j++)
            add(i,a[i][j]+n,1),add(a[i][j]+n,i,0);
    for (int i=1;i<=m;i++) add(n+i,n+n+1,b[i]),add(n+n+1,n+i,0);
}
bool bfs(int u)
{
    for(int i=0;i<=n+n+n;i++) cur[i]=h[i]; 
    memset(d,0,sizeof(d));
    memset(q,0,sizeof(q));
    int head=0,tail=0;
    d[u]=1;
    q[++tail]=u;
    while (head<tail)
    {
        head++;
        for (int i=h[q[head]];i;i=e[i].nxt)
        {
            int v=e[i].to;
            if (d[v]!=0||e[i].z<=0) continue;
            d[v]=d[q[head]]+1;
            q[++tail]=v;
        }
    }
    if (d[n+n+1]>0) return true;
    else return false;
}
int dinic(int u,int mn,int fa)
{
    if (u==n+n+1||mn==0) return mn;
    int sum=0,flow=0;
    for (int i=cur[u];i;i=e[i].nxt)
    {
        cur[u]=i;//本代码加入了当前弧优化
        int v=e[i].to;
        if (v==fa||d[v]!=d[u]+1||e[i].z<=0) continue;
        sum=dinic(v,min(mn,e[i].z),u);
        if (sum<=0) continue;
        flow+=sum;
        mn-=sum;
        e[i].z-=sum;
        e[i^1].z+=sum;
        if (mn<=0) break;
    }
    return flow;
}
bool opt()
{
    int res=0;
    while (bfs(0)==true)
        res+=dinic(0,2147483647,0);
    if (res==n) return true;
    else return false;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    for (int i=1;i<=m;i++)
        scanf("%d",&b[i]);
    int ans=2147483647;
    for (int i=1;i<=m;i++)
    {
        int l=0,r=m-i;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            build(i,i+mid);
            if (opt()==true) ans=min(ans,mid+1),r=mid-1;
            else l=mid+1;
        }   
    }   
    printf("%d
",ans);
} 

以上是关于牛棚安排的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 3389: [Usaco2004 Dec]Cleaning Shifts安排值班 -- 贪心

bzoj1672[Usaco2005 Dec]Cleaning Shifts 清理牛棚*

洛谷 P1209 修理牛棚== Codevs 2079 修理牛棚

38 修理牛棚

修理牛棚

12178. 破坏牛棚