题意:有C个奶牛去晒太阳 (1 <=C <= 2500),每个奶牛各自能够忍受的阳光强度有一个最小值和一个最大值,太大就晒伤了,太小奶牛没感觉。刚开始的阳光的强度非常大,奶牛都承受不住,然后奶牛就得涂抹防晒霜,防晒霜的作用是让阳光照在身上的阳光强度固定为某个值。给出了L种防晒霜中每种的数量和固定的阳光强度。每个奶牛只能抹一瓶防晒霜,最后问能够享受晒太阳的奶牛最多有几个。
思路:一个显然的思路是用最大流来做,牛作为一部,防晒霜作为一部,防晒霜适用于牛则连边,流量为1。防晒霜到汇点流量为该防晒霜的数量,求最大流。
回忆算法课贪心那章的作业第一题:给出n个区间和n个点,问这n个区间能否和n个点一一对应。当时老师讲了一种nlogn的方法,就是区间按照左端点排序,点也排序,然后从左往右扫一遍点,对于每个点,将左端点小于等于该点的区间入堆(堆按照区间右端点比较,且为小顶堆)。此时将堆顶的元素拿出来作为匹配该点的区间,以此类推。如果处理某点时堆为空,那么必然不存在一一对应。这种贪心思想可以通过交换论证来证明。
那么回到这道题发现类似,只不过区间和点的数量并不是同样多的,但是做法是相同的。而且最后进行处理部分的均摊复杂度为O(C+L),整个的复杂度为O(max(ClogC,LlogL))。
最大流思路:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 2505
int a[N],b[N],num[N],w[N];
struct edge{
int y,c,next;
}e[N*N*2+N*4];
int first[N<<1],top,flag[N<<1],f[N<<1];
int n,m;
void add(int x,int y,int c){
e[top].y = y;
e[top].c = c;
e[top].next = first[x];
first[x] = top++;
}
int bfs(int s,int t){
int i,now;
queue<int> q;
q.push(s);
clc(flag, -1);
flag[s] = 0;
while(!q.empty()){
now = q.front();
q.pop();
for(i = first[now];i!=-1;i=e[i].next)
if(flag[e[i].y] == -1 && e[i].c>0){
flag[e[i].y] = flag[now]+1;
if(e[i].y == t)
return 1;
q.push(e[i].y);
}
}
return 0;
}
int dfs(int x,int t,int a){
int i,j,res=0;
if(x==t || !a)
return a;
for(i = f[x];i!=-1;i=f[x]=e[i].next)
if(flag[e[i].y] == flag[x]+1){
j = dfs(e[i].y,t,min(a,e[i].c));
if(!j) continue;
res += j;
e[i].c -= j;
e[i^1].c += j;
a -= j;
if(!a) break;
}
return res;
}
int dinic(int s,int t){
int res = 0;
while(bfs(s,t)){
memcpy(f, first, sizeof(first));
res += dfs(s,t,INF);
}
return res;
}
int main(){
int i,j;
clc(first,-1);
top = 0;
scanf("%d %d",&n,&m);
for(i = 1;i<=n;i++)
scanf("%d %d",&a[i],&b[i]);
for(i = 1;i<=m;i++)
scanf("%d %d",&w[i],&num[i]);
for(i = 1;i<=n;i++)
for(j = 1;j<=m;j++)
if(w[j]>=a[i] && w[j]<=b[i])
add(i,j+n,1),add(j+n,i,0);
for(i = 1;i<=n;i++)
add(0,i,1),add(i,0,0);
for(i = 1;i<=m;i++)
add(i+n,n+m+1,num[i]),add(n+m+1,i+n,0);
printf("%d\n",dinic(0,n+m+1));
return 0;
}
贪心思路:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
#define INF 0x3fffffff
#define clr(s,t) memset(s,t,sizeof(s))
#define N 2505
struct interval{
int a,b;
bool operator<(const interval x)const{
return x.b < b;
}
}s[N];
struct line{
int x,num;
}t[N];
int n,m;
priority_queue<struct interval> q;
int cmp1(interval x,interval y){
return x.a < y.a;
}
int cmp2(line a,line b){
return a.x < b.x;
}
int main(){
int i,j,res=0;
scanf("%d %d",&n,&m);
for(i = 0;i<n;i++)
scanf("%d %d",&s[i].a,&s[i].b);
for(i = 0;i<m;i++)
scanf("%d %d",&t[i].x,&t[i].num);
sort(s,s+n,cmp1);
sort(t,t+m,cmp2);
for(i = j = 0;i<m;i++){
for(;j<n&&s[j].a<=t[i].x;j++)
q.push(s[j]);
while(!q.empty()){//先把肯定不符合的弹出来
if(q.top().b >= t[i].x)
break;
q.pop();
}
while((t[i].num--) && !q.empty()){//这是在堆里的都是必然符合的
q.pop();
res++;
}
}
printf("%d\n",res);
return 0;
}