题意:有n个人,m个洞。每个洞能容纳一个人,每个人到每个洞需要花费一些时间。每个人到达一个洞后可以花C的时间来挖一个洞,并且最多挖一个洞,这样又能多容纳一人。求能使至少K个人进洞的最短时间。
解法:看到n个人和m个洞,并且人要进洞容易想到二分匹配,又是求极值的问题,应该是最大匹配。由于直接求极值不好求,可以将求极值问题转化为判定问题,即二分最短时间,然后判定能否达到。判定时,如果i到j的时间小于等于mid,就将i和j连一条边,如果T[i][j]+C <= mid 说明还来得及挖洞,将i和j+m连一条边,j+m为新挖的洞,每次判定要建一次图,然后求一个最大匹配,如果最大匹配数>=k,说明mid可行,继续往下二分。直到找到答案。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> using namespace std; #define N 107 vector<int> G[N]; int vis[2*N],match[2*N]; int mp[N][N]; int n,m,k,c; int Search_Path(int s) { for(int i=0;i<G[s].size();i++) { int v = G[s][i]; if(!vis[v]) { vis[v] = 1; if(match[v] == -1 || Search_Path(match[v])) { match[v] = s; return 1; } } } return 0; } int Max_match() { memset(match,-1,sizeof(match)); int cnt = 0; for(int i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); if(Search_Path(i)) cnt++; } return cnt; } int check(int mid) { int i,j; for(i=0;i<=n;i++) G[i].clear(); for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { if(mp[i][j] <= mid) G[i].push_back(j); if(mp[i][j]+c <= mid) G[i].push_back(j+m); } } int cnt = Max_match(); if(cnt >= k) return 1; return 0; } int main() { int t,i,j; scanf("%d",&t); while(t--) { scanf("%d%d%d%d",&n,&m,&k,&c); int maxi = -1; for(i=1;i<=n;i++) for(j=1;j<=m;j++) { scanf("%d",&mp[i][j]); maxi = max(mp[i][j],maxi); } int low = 0; int high = maxi; while(low<=high) { int mid = (low+high)/2; if(check(mid)) high = mid-1; else low = mid+1; } printf("%d ",high+1); } return 0; }