题目链接:http://codeforces.com/problemset/problem/589/F
题意:有n盆菜,每个菜的上菜时间是ai-bi,要求每盆菜的品尝时间都相同,求最大的品尝时间。
思路:一开始也想到了网络流,但是没有想到压缩区域。。。(我只想到一个点一个点地建图,这个的图点可能有10^4个啊。。肯定不能用网络流),后来请教别人可以一个区间建图,流量就是区间长度,这个的话点最多300+个。。。网络流妥妥可以。。。。
做法就是找到一个区间,扫一遍所有的菜哪些菜的上菜时间是包含这个区间的,然后从s到时间区间点连一个区间长度大小的流量,时间区间点到能到的菜连一个区间长度大小的流量,最后一遍最大流搞定。
PS:神奇的贪心做法可以二分最大品尝时间,每次扫描一个单位的区间然后把这个区间分给bi最小且品尝时间没满的菜,就这样好了。。。。
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define For(a,b,c) for(int a = b;a <= c;a++)
using namespace std;
typedef long long ll;
const int maxn = 1000005;
const int INF = 0x3f3f3f3f;
const int e_maxn = 100000 * 4;
const int v_maxn = 500;
struct ppp
{
int v,nex,cap,flow,c;//分别为下一个点,下一条边,当前边容量和流量,还有费用
}e[e_maxn];
int head[v_maxn],dis[v_maxn],cur[v_maxn];//链表头节点,层次图中每个点所属层次,dfs所用的临时链表头
int tole,N,M,s,t;//总加入边数,总输入点数和总输入边数,源点和汇点
void make_edge(int u,int v,int cap,int c)//求最大流时费用不必考虑,可以省去,加上也没关系
{
e[tole].v = v;e[tole].flow = 0;e[tole].cap = cap;e[tole].c = c;e[tole].nex = head[u];head[u] = tole++;
}
void add_edge(int u,int v,int cap,int c)
{
make_edge(u,v,cap,c);//正向边容量为cap,费用为c
make_edge(v,u,0,-c);//反向边容量为0,费用为-c
}
int bfs()//其中s为源点,t为汇点
{
queue<int> que;
que.push(s);
mem(dis,-1);
dis[s] = 0;
int temp,v;
while(!que.empty())
{
temp = que.front();
que.pop();
for(int i = head[temp];~i;i = e[i].nex)
{
v = e[i].v;
if(dis[v] == -1 && e[i].cap > e[i].flow)
{
dis[v] = dis[temp] + 1;
que.push(v);
}
}
}
return dis[t] != -1;
}
int dfs(int x,int a)
{
if(x == t || !a) return a;
int v,f,ret = 0;
for(int &i = cur[x];~i;i = e[i].nex)
{
v = e[i].v;
if((dis[v] == dis[x] + 1) && (f = dfs(v,min(a,e[i].cap - e[i].flow))) > 0)
{
e[i].flow += f;
e[i ^ 1].flow -= f;
a -= f;
ret += f;
if(!a)break;
}
}
return ret;
}
void Dinic(int &ans)//ans为最大流
{
while(bfs())
{
for(int i = 0;i <= t;i++)cur[i] = head[i];
ans += dfs(s,INF);
}
}
int mark[10005];
int n;
int a[105],b[105];
int cal(int x)
{
s = 0,t = v_maxn - 1;
mem(head,-1);
tole = 0;
int from = 0;
int cnt = 0;
for(int i = 0;i <= 10000;i++){
if(mark[i]){
int ok = 0;
for(int j = 0;j < n;j++){
if(a[j] <= from && i <= b[j])
{
if(!ok){
cnt++;
add_edge(s,cnt,i - from,0);
ok = 1;
}
add_edge(cnt,300 + j,i - from,0);
}
}
from = i;
}
}
for(int i = 0;i < n;i++)
add_edge(300 + i,t,x,0);
int ans = 0;
Dinic(ans);
if(ans == x * n)return 1;
else return 0;
}
int main()
{
while(~scanf("%d",&n))
{
mem(mark,0);
int minn = 999999999;
for(int i = 0;i < n;i++){
scanf("%d%d",&a[i],&b[i]);
mark[a[i]] = 1;mark[b[i]] = 1;
minn = min(minn,b[i] - a[i]);
}
int l = 0,r = minn;
int ans = 0;
while(l <= r)
{
int mid = (l + r) >> 1;
if(cal(mid)){
ans = mid;
l = mid + 1;
}else r = mid - 1;
}
printf("%d\n",ans * n);
}
}