1. 程式人生 > 實用技巧 >洛谷P2857 [USACO06FEB]Steady Cow Assignment G

洛谷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;
}