[POJ3189][cqbzoj1640]稳定的奶牛分配 解题报告

[POJ3189]稳定的奶牛分配

时间限制: 1 Sec 内存限制: 64 MB

题目描述

农夫约翰有N(1<=N<=1000)只奶牛,每只奶牛住在B(1<=B<=20)个奶牛棚中的一个。当然,奶牛棚的容量有限。有些奶牛对它现在住的奶牛棚很满意,有些就不太满意了。

农夫约翰想要重新安排这些奶牛,使得奶牛的满意度尽可能相同,尽管有可能这意味者所有的奶牛都不喜欢新分配的奶牛棚。

每只奶牛都按顺序给出她喜欢的奶牛棚。在某个分配方案中,一只奶牛的满意度等于她对她的奶牛棚的评价等级。你的工作是找出一种分配方案使得没有奶牛棚超出它的容量,而且奶牛给分配到的奶牛棚的评价等级的范围(即分配到的等级最高的奶牛棚和等级最低的奶牛棚之间的差值+1)尽可能的小。

附上原文:the size of the range (i.e., one more than the positive difference between the the highest-ranked barn chosen and that lowest-ranked barn chosen) of barn rankings the cows give their assigned barns is as small as possible.

输入

第1行:两个用空格隔开的整数,N和B
第2..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.枚举答案上界u与下界d(就是奶牛的满意度上界和下界)
2.按照u和d建图
建图方法:
- 1.每个奶牛和每个牛棚代表一个节点
- 2.源点向每头奶牛连边,边权为1 代表这头奶牛只能使用一次
- 3.每头奶牛向这头奶牛选择中满意度在[u,d]之间的牛棚连一条权值为1的边
4.每个牛棚向汇点连一条权值为牛棚容量的边
3.跑最大流
4.判断flow是否等于奶牛数量 如果相等,ans=min(ans,d-u+1);

如果把枚举换成二分答案建图跑得飞快
但是原谅我太懒提供不了二分版本答案(. V . )

接下来是代码

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
namespace isap{
const int MAXN=2000;
const int MAXM=1000000;
const int INF=0x7f7f7f7f;
const int INF4BIT=0x7f;
struct Node{
    int v,c,nxt,bk;
}nodes[MAXM];
int head[MAXN],e_total;
int n,S,T;
inline void addedge(int a,int b,int c){
    e_total++;
    nodes[e_total].v=b;
    nodes[e_total].c=c;
    nodes[e_total].nxt=head[a];
    nodes[e_total].bk=e_total+1;
    head[a]=e_total;
    e_total++;
    nodes[e_total].v=a;
    nodes[e_total].c=0;
    nodes[e_total].nxt=head[b];
    nodes[e_total].bk=e_total-1;
    head[b]=e_total;
}
bool vis[MAXN];
int d[MAXN],vd[MAXN];
void spfa(int start){
    queue<int>que;
    que.push(start);
    vis[start]=true;
    while(!que.empty()){
        int u=que.front();
        que.pop();
        vis[u]=false;
        for(int e=head[u];e;e=nodes[e].nxt)
            if(d[nodes[e].v]>d[u]+1){
                d[nodes[e].v]=d[u]+1;
                if(!vis[nodes[e].v]){
                    vis[nodes[e].v]=true;
                    que.push(nodes[e].v);
                }
            }
    }
    for(int i=1;i<=n;i++)
        if(d[i]<=n)vd[d[i]]++;
}
int aug(int u,int augv){
    if(u==T)return augv;
    int last=augv;
    for(int e=head[u];e;e=nodes[e].nxt)
        if(nodes[e].c&&d[nodes[e].v]+1==d[u]){
            int t=aug(nodes[e].v,min(nodes[e].c,last));
            nodes[e].c-=t;
            nodes[nodes[e].bk].c+=t;
            last-=t;
            if(d[S]>=n||!last)return augv-last;
        }
    if(augv==last){
        vd[d[u]]--;
        if(!vd[d[u]]){d[S]=n+1;return 0;}
        d[u]=n+1;
        for(int e=head[u];e;e=nodes[e].nxt)
            if(nodes[e].c)d[u]=min(d[u],d[nodes[e].v]+1);
        vd[d[u]]++;
    }
    return augv-last;
}
int sap(){
    for(int i=1;i<=n;i++)
        d[i]=INF;
    d[T]=0;
    spfa(T);
    int flow=0;
    while(d[S]<n)flow+=aug(S,INF);
    return flow;
}
void clear(){
    memset(nodes,0,sizeof nodes);
    memset(vis,0,sizeof vis);
    memset(head,0,sizeof head);
    e_total=0;
    memset(vd,0,sizeof vd);
}
}
int n,m;
int inp[1200][30];
int bns[30];
void build(int l,int r){
    isap::S=1;
    isap::T=isap::S+n+m+1;
    isap::n=isap::T;
    for(int i=1;i<=n;i++){
        isap::addedge(isap::S,isap::S+i,1);
        for(int j=l;j<=r;j++)
            isap::addedge(isap::S+i,isap::S+n+inp[i][j],1);
    }
    for(int i=1;i<=m;i++)
        isap::addedge(isap::S+n+i,isap::T,bns[i]);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&inp[i][j]);
    for(int i=1;i<=m;i++)
        scanf("%d",&bns[i]);
    int ans=m;
    for(int i=1;i<=m;i++)
        for(int j=i;j<=m;j++){
            if(j-i+1>=ans)continue;
            isap::clear();
            build(i,j);
            int t=isap::sap();
            if(t>=n)ans=min(j-i+1,ans);
        }
    printf("%d\n",ans);
}

转载于:https://www.cnblogs.com/Hineven/p/5843567.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值