题意:有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; }