洛谷P2857 [USACO06FEB]Steady Cow Assignment G
阿新 • • 發佈:2020-12-11
題目
https://www.luogu.com.cn/problem/P2857
思路
直接思考不太好處理,也沒有對應的模型可以套。那麼我們考慮列舉最終答案,並通過一個check函式判斷答案的可行性。
列舉區間跨度還不夠,我們還需要列舉區間起點,這樣,原問題就變成:能否找到一個分配方案,使每頭牛的對所屬牛棚的評價(就這麼說吧,原題說法太拗口)屬於\([l,r]\)這個區間。
考慮牛與牛棚的關係,如果牛\(i\)對棚\(j\)的評價在\([l,r]\)區間內,那麼\(i\)可以放入\(j\),否則不行。簡而言之,就是:有N個物體,B個盒子,已知每個物體只能放入哪些盒子,求是否能將所有物體放入盒子。
那麼問題就簡單了,我們把牛與棚看成結點,如果牛\(i\)
雖說要列舉的次數不多,但是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; }