poj 3614 dinic/贪心 (涂防晒霜)

题意:有C个奶牛去晒太阳 (1 <=C <= 2500),每个奶牛各自能够忍受的阳光强度有一个最小值和一个最大值,太大就晒伤了,太小奶牛没感觉。刚开始的阳光的强度非常大,奶牛都承受不住,然后奶牛就得涂抹防晒霜,防晒霜的作用是让阳光照在身上的阳光强度固定为某个值。给出了L种防晒霜中每种的数量和固定的阳光强度。每个奶牛只能抹一瓶防晒霜,最后问能够享受晒太阳的奶牛最多有几个。

思路:一个显然的思路是用最大流来做,牛作为一部,防晒霜作为一部,防晒霜适用于牛则连边,流量为1。防晒霜到汇点流量为该防晒霜的数量,求最大流。

回忆算法课贪心那章的作业第一题:给出n个区间和n个点,问这n个区间能否和n个点一一对应。当时老师讲了一种nlogn的方法,就是区间按照左端点排序,点也排序,然后从左往右扫一遍点,对于每个点,将左端点小于等于该点的区间入堆(堆按照区间右端点比较,且为小顶堆)。此时将堆顶的元素拿出来作为匹配该点的区间,以此类推。如果处理某点时堆为空,那么必然不存在一一对应。这种贪心思想可以通过交换论证来证明。

那么回到这道题发现类似,只不过区间和点的数量并不是同样多的,但是做法是相同的。而且最后进行处理部分的均摊复杂度为O(C+L),整个的复杂度为O(max(ClogC,LlogL))。

最大流思路:

#include 
#include 
#include 
#include 
#include 
#include 
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 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 
#include 
#include 
#include 
#include 
#include 
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 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= t[i].x)
                break;
            q.pop();
        }
        while((t[i].num--) && !q.empty()){//这是在堆里的都是必然符合的
            q.pop();
            res++;
        }
    }
    printf("%d\n",res);
    return 0;
}


你可能感兴趣的:(贪心,图论——网络流)