【USACO】Optimal Milking

题目链接 :

       【POJ】点击打开链接

       【caioj】点击打开链接

算法 :

1:跑一遍弗洛伊德,求出点与点之间的最短路径

2:二分答案,二分”最大值最小“

3.1:建边,将原点与每头奶牛连边,流量为1,记dist[i][j]为i到j的最短路径,若dist[i][j]<=mid (K+1<=i<=K+C,1<=j<=K),则将i与j连边,流量为M,将每台挤奶机与汇点连边,流量为1

3.2 : 跑网络流,这里笔者使用的是dinic算法

3.3 : 判断最大流S是否等于K,等于K,则往小搜,否则往大搜

代码 :

#include <algorithm>
#include <bitset>
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <limits>
#include <list>
#include <map>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <utility>
#include <vector>
#include <cwchar>
#include <cwctype>
#include <stack>
#include <limits.h>
using namespace std;
#define MAXK 30
#define MAXC 200
#define MAXM 15

typedef long long LL;

LL i,j,low,high,mid,st,ed,K,C,M,tot,ans;
LL h[MAXK+MAXC+10],dist[MAXK+MAXC+10][MAXK+MAXC+10],
	 U[MAXC*100+10],V[MAXC*100+10],W[MAXC*100+10],Head[MAXC*100+10],
	 Next[MAXC*100+10],other[MAXC*100+10];
		
template <typename T> inline void read(T &x) {
		LL f = 1; x = 0;
		char c = getchar(); 
		for (; !isdigit(c); c = getchar()) { if (c=='-') f = -f; }
		for (; isdigit(c); c = getchar()) x=x*10+c-'0';
		x*=f;
}

template <typename T> inline void write(T x) {
		if (x < 0) { putchar('-'); x = -x; }
		if (x > 9) write(x/10);
		putchar(x % 10 + '0');	
}

template <typename T> inline void writeln(T x) {
		write(x);
		puts("");	
}

inline void floyed() {
		LL i,j,k;
		for (k = 1; k <= K + C; k++) {
			  for (i = 1; i <= K + C; i++) {
						if (i == k) continue;
						for (j = 1; j <= K + C; j++) {
								if ((k == j) || (i == j)) continue;
								dist[i][j] = min(dist[i][j],dist[i][k]+dist[k][j]);							
						}	
				}
		}		
}

inline void add(LL a,LL b,LL c) {
		++tot;
		U[tot] = a; V[tot] = b; W[tot] = c;
		Next[tot] = Head[a]; Head[a] = tot; 
		other[tot] = ++tot;
		U[tot] = b; V[tot] = a; W[tot] = 0;
		Next[tot] = Head[b]; Head[b] = tot;
		other[tot] = tot - 1;	
}

inline bool BFS() {
		LL i,x,y;
		queue<LL> q;
		memset(h,0,sizeof(h));
		h[st] = 1; q.push(st);
		while (!q.empty()) {
				x = q.front(); q.pop();
				for (i = Head[x]; i; i = Next[i]) {
						y = V[i];
						if ((W[i] > 0) && (!h[y])) {
								h[y] = h[x] + 1;
								q.push(y);
						}
				}
		}
		if (h[ed]) return true;
		else return false;
}

inline LL maxflow(LL x,LL f) {
		LL i,t,y,sum=0;
		if (x == ed) return f;
		for (i = Head[x]; i; i = Next[i]) {
				y = V[i];
				if ((W[i] > 0) && (h[y] == h[x] + 1) && (sum < f)) {
						sum += (t = maxflow(y,min(W[i],f-sum)));
						W[i] -= t; W[other[i]] += t;
				} 	
		}	
		if (!sum) h[x] = 0;
		return sum;
}

inline bool check(LL ml) {
		LL i,j,sum=0;
		tot = 0;
		memset(Head,0,sizeof(Head));
		for (i = K + 1; i <= K + C; i++) {
				for (j = 1; j <= K; j++) {
						if (dist[i][j] <= ml)
								add(i,j,1);
				}
		}	
		for (i = K + 1; i <= K + C; i++) add(st,i,1); 
		for (i = 1; i <= K; i++) add(i,ed,M);
		while (BFS()) {
				sum += maxflow(st,C);
		}
		return sum == C;
}

int main() {
		
		read(K); read(C); read(M);
		st = K + C + 1; ed = st + 1;
		
		for (i = 1; i <= K + C; i++) {
				for (j = 1; j <= K + C; j++) {
						read(dist[i][j]);
						if (!dist[i][j]) dist[i][j] = 2e9;
				}	
		}	
		
		floyed();
		
		for (i = K + 1; i <= K + C; i++) {
				for (j = 1; j <= K; j++) {
						if (dist[i][j] != 2e9)
								high = max(high,dist[i][j]);
				}	
		}	
		
		low = 1; 
		
		while (low <= high) {
				mid = (low + high) >> 1;
				if (check(mid)) {
						high = mid - 1;
						ans = mid;
				}	else
						low = mid + 1;
		}
		writeln(ans);
		
		return 0;
	
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值