洛谷P2857 [USACO06FEB]Steady Cow Assignment G

题目

https://www.luogu.com.cn/problem/P2857

思路

直接思考不太好处理,也没有对应的模型可以套。那么我们考虑枚举最终答案,并通过一个check函数判断答案的可行性。

枚举区间跨度还不够,我们还需要枚举区间起点,这样,原问题就变成:能否找到一个分配方案,使每头牛的对所属牛棚的评价(就这么说吧,原题说法太拗口)属于([l,r])这个区间。

考虑牛与牛棚的关系,如果牛(i)对棚(j)的评价在([l,r])区间内,那么(i)可以放入(j),否则不行。简而言之,就是:有N个物体,B个盒子,已知每个物体只能放入哪些盒子,求是否能将所有物体放入盒子。

那么问题就简单了,我们把牛与棚看成结点,如果牛(i)可以放入棚(j),那么在(i)(j)间连一条有向边。建个源点,汇点,跑最大流,检验最大流是否为N就可以了。

虽说要枚举的次数不多,但是check函数的复杂度巨大,建议还是二分区间长度吧。

至于check函数,学长说可以用Hopcroft-Karp,但我感觉用现学的算法写非模板题不太好,就还是用了O(玄学)的dinic,仍然能过233.

注意事项:输入很坑,第(i+1)行第(j)列并不表示牛棚(j)在牛(i)眼里的位次,而是代表在牛(i)眼里位次为(j)的棚的序号!!!

代码

#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
int num[21],Rank[1001][21],n,m,S,T;
int fst[1200],nxt[41000],cnt=0,deep[1200];
struct edge{
    int u,v,cap;
} e[41000];
int inv(int x){
    if(x&1) return x+1;
    else return x-1;
}
void add(int x,int y,int z){
    e[++cnt].u=x;
    e[cnt].v=y;
    e[cnt].cap=z;
    nxt[cnt]=fst[x];
    fst[x]=cnt;
}
void build(int l,int r){
    int i,j;
    S=n+m+1;T=n+m+2;cnt=0;
    memset(fst,0,sizeof(fst));
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            if(Rank[i][j]>=l&&Rank[i][j]<=r){
                add(i,n+j,1);
                add(n+j,i,0);
            }
        }
    }
    for(i=1;i<=n;i++){
        add(S,i,1);
        add(i,S,0);
    }
    for(i=1;i<=m;i++){
        add(n+i,T,num[i]);
        add(T,n+i,0);
    }
}
int bfs(){
    queue<int> q;
    int i,flag=0;
    memset(deep,0,sizeof(deep));
    q.push(S);deep[S]=1;
    while(!q.empty()){
        int p=q.front();
        if(p==T) flag=1;
        for(i=fst[p];i;i=nxt[i]){
            if(deep[e[i].v]||!e[i].cap) continue;
            deep[e[i].v]=deep[p]+1;
            q.push(e[i].v);
        }
        q.pop();
    }
    return flag;
}
int dfs(int x,int flow){
    int i;
    if(x==T) return flow;
    for(i=fst[x];i;i=nxt[i]){
        if(deep[e[i].v]==deep[x]+1){
            if(e[i].cap){
                int tmp=dfs(e[i].v,min(flow,e[i].cap));
                if(tmp){
                    e[i].cap-=tmp;
                    e[inv(i)].cap+=tmp;
                    return tmp;
                }
            }
        }
    }
    return 0;
}
int dinic(){
    int i,ans=0;
    while(bfs()){
        int flow;
        while(flow=dfs(S,inf)){
            ans+=flow;
        }
    }
    return ans;
}
bool check(int x){
    int i,j,k;
    for(i=1;i+x<=m;++i){
        build(i,i+x);
        if(dinic()==n) return true;
    }
    return false;
}
int main(){
    int i,j,x;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;++i){
        for(j=1;j<=m;++j){
            scanf("%d",&x);
            Rank[i][x]=j;
        }       
    }
    for(i=1;i<=m;++i)
        scanf("%d",&num[i]);
    int l=0,r=m-1;
    while(l<r){
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    printf("%d",l+1);
    // system("pause");
    return 0;
}
原文地址:https://www.cnblogs.com/landmine-sweeper/p/14117822.html