网络流。
建图方法:
超级源到每人各一条容量为1的边,将洞拆成两个i和i+100,每个洞到超级汇各一条容量为1的边。
二分答案,如果人到洞的时间小于之,则加一条边到 i,若人到洞的时间+挖洞时间小于二分的答案,则又加一条边到 i+100。
最后判流量即可。
//time:1460ms
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int M=30010;
const int Mx=10000000;
int n,m,k,c;
int map[110][110];
struct Edge
{
Edge(int a,int b,int c,int d) {
from=a,to=b;
cap=c,flow=d;
}
int from,to,cap,flow;
};
vector<Edge> edge;
vector<int> G[310];
bool vis[310];
int d[310];
int cur[310];
int s=301,t=302;
void AddEdge(int from,int to,int cap)
{
edge.push_back(Edge(from,to,cap,0));
edge.push_back(Edge(to,from,0,0));
int m=edge.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS()
{
memset(vis,0,sizeof vis);
queue<int> q;
q.push(s);
vis[s]=1;
while(!q.empty()) {
int x=q.front();
q.pop();
for(int i=0;i<G[x].size();++i) {
Edge& e=edge[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow) {
vis[e.to]=1;
d[e.to]=d[x]+1;
q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a)
{
if(x==t||0==a) return a;
int flow=0,f;
for(int& i=cur[x];i<G[x].size();++i) {
Edge& e=edge[G[x][i]];
if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0) {
e.flow+=f;
edge[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(0==a) break;
}
}
return flow;
}
int Maxflow()
{
int flow=0;
while(BFS()) {
memset(cur,0,sizeof cur);
flow+=DFS(s,Mx);
}
return flow;
}
bool work(int mid)
{
edge.clear();
for(int i=0;i<310;++i)
G[i].clear();
for(int i=1;i<=n;++i) {
for(int j=1;j<=m;++j) {
if(map[i][j]<=mid) AddEdge(i,j+100,1);
if(map[i][j]+c<=mid) AddEdge(i,200+j,1);
}
}
for(int i=1;i<=n;++i)
AddEdge(s,i,1);
for(int i=1;i<=m;++i) {
AddEdge(i+100,t,1);
AddEdge(i+200,t,1);
}
int mf=Maxflow();
return mf>=k;
}
int main()
{
int _;
int L,R,mid;
scanf("%d",&_);
while(_--) {
scanf("%d%d%d%d",&n,&m,&k,&c);
L=1;R=Mx;
for(int i=1;i<=n;++i) {
for(int j=1;j<=m;++j)
scanf("%d",&map[i][j]);
}
while(L<R) {
mid=(R+L)>>1;
if(work(mid)) R=mid;
else L=mid+1;
}
printf("%d\n",R);
}
}