P2857 [USACO06FEB] Steady Cow Assignment G
Introduction
第一次写二分网络流,之前总感觉二分和网络流融合不了。
Sol
注意到如果当前的跨度 \(x\) 可行,那么比 \(x\) 大的跨度也可行,反之亦然,因此可以二分。
因为数据较小,所以我们可以想到网络流。
我们考虑枚举跨度的区间的位置,然后如下建模:
- 源点 \(s\) 向所以牛建边,流量为 \(1\),代表每头牛只能在一个牛棚。
- 对于每头牛,向在跨度区间内的所有牛棚建一条边,流量可以设任意 \(\ge 1\) 的整数,代表一头牛可以在某个牛棚里,流量不限是因为源点到所有的牛的流量为 \(1\),和后面的就没有影响。
- 所以牛棚向汇点 \(t\) 连边,流量为最多能容纳的牛数,代表至多这么多牛在这个牛棚里。
跑最大流,得到的就是最多有多少头牛可以在牛棚里,当这个数为 \(n\) 时,这个跨度是可以的。
一个跨度有任意一个区间可行,那么它就是可行的。
最后二分出来的即为答案。
时间复杂度分析:
网络流只有 \(4\) 层,所以 Dinic 肯定跑不满。
注意到将中间的边流量设为 \(1\) 时,不为 \(1\) 的流量非常少,只有 \(B \le 20\) 条边。
所以时间复杂度约为 \(O((n \sqrt{n} + nB) B \log B)\),而且跑不满,很难卡。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
int x(0);
short f(1);
char ch(getchar());
while(!isdigit(ch))f=(ch=='-')?-1:1,ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
const int N=3005;
class eg
{
public:
int to,w;
int unsigned ip;
};
int n,m,s,t,a[N][N],R[N],dep[N],hig[N];
vector<eg>nbr[N];
bool bfs()
{
queue<int>q;
q.push(s);
for(int i=s;i<=t;i++)
dep[i]=-1,hig[i]=0;
dep[s]=0;
while(q.empty()==false)
{
int cur=q.front();
q.pop();
for(auto dat:nbr[cur])
{
int nxt=dat.to,w=dat.w;
if(w>0&&dep[nxt]==-1)
{
dep[nxt]=dep[cur]+1;
q.push(nxt);
}
}
}
return dep[t]>=0;
}
int dfs(int cur,int now)
{
if(cur==t)
return now;
int num=0;
for(int i=hig[cur];i<nbr[cur].size();i=hig[cur])
{
hig[cur]=i+1;
int nxt=nbr[cur][i].to,w=nbr[cur][i].w,ip=nbr[cur][i].ip;
if(w>0&&dep[nxt]==dep[cur]+1)
{
int h=dfs(nxt,min(now,w));
nbr[cur][i].w-=h;
nbr[nxt][ip].w+=h;
now-=h;
num+=h;
if(now==0)
return num;
}
}
return num;
}
void add(int x,int y,int w=1)
{
nbr[x].push_back({y,w,nbr[y].size()});
nbr[y].push_back({x,0,nbr[x].size()-1ull});
return ;
}
bool solve(int lt,int rt)
{
s=0,t=n+m+1;
for(int i=s;i<=t;i++)
nbr[i].clear();
for(int i=1;i<=n;i++)
add(s,i);
for(int i=n+1;i<=n+m;i++)
add(i,t,R[i-n]);
for(int i=1;i<=n;i++)
for(int j=lt;j<=rt;j++)
add(i,a[i][j]+n);
int ans=0;
while(bfs()==true)
ans+=dfs(s,2e18);
return ans==n;
}
bool check(int x)
{
for(int pos=1;pos+x-1<=m;pos++)
if(solve(pos,pos+x-1)==true)
return true;
return false;
}
signed main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=read();
for(int i=1;i<=m;i++)
R[i]=read();
int lt=0,rt=m;
while(lt+1<rt)
{
int mid=(lt+rt)>>1;
if(check(mid)==true)
rt=mid;
else
lt=mid;
}
cout<<rt;
return 0;
}

浙公网安备 33010602011771号