HDU 6437 Problem L.Videos (最大费用)【费用流】

<题目链接>

题目大意:

一天有N个小时,有m个节目(每种节目都有类型),有k个人,连续看相同类型的节目会扣w快乐值。每一种节目有都一个播放区间[l,r]。每个人同一时间只能看一个节目,看完可以获得快乐值,每个节目只能被人看一次。问最多可以获得多少快乐?

解题分析:

本题用费用流求解的方式还是比较直观的。因为本题要求的是最大费用,所以我们需要建图的时候需要将所有实际费用取反,然后将最后最小费用最大流求出的答案取反,就是要求的最大费用。具体建图过程见代码:

#include <bits/stdc++.h>
using namespace std;

#define clr(a,b) memset(a,b,sizeof(a))
template<typename T>
inline void read(T&x){
    x=0;int f=1;char c=getchar();
    while(c<'0' || c>'9'){ if(c=='-')f=-1;c=getchar(); }
    while(c>='0' && c<='9'){ x=x*10+c-'0';c=getchar(); }
    x*=f;
}

//最小费用最大流模板
struct MCMF{
    static const int N = 5e4+100, M = 5e5+5, INF = 0x7fffffff; 

    int n, m, head[N], s, t, cnt;
    int dist[N], inq[N], pre[N], incf[N];
    int mxflow, mncost;
    struct Edge{ int to, cap, cost, next; } edge[M * 4];

    inline void init(int l, int r, int ss, int tt){
        //for(int i = l; i <= r; i++ ){head[i] = -1;}
        memset(head, -1, sizeof(head));
        s = ss, t = tt, cnt = 1;
        mxflow = mncost = 0;
    }
    inline void add(int u,int v,int cap,int cost){
        edge[++cnt]=(Edge){v,cap,cost,head[u]};head[u]=cnt;
        edge[++cnt]=(Edge){u,0,-cost,head[v]};head[v]=cnt;
    }

    bool spfa(int s, int t){       //slf优化的spfa(双端队列优化)
        for(int i = 0; i < N; i++)
            dist[i] = INF, inq[i] = 0;
        clr(pre,0);
        deque<int>q;
        inq[s]=1,dist[s] = 0;
        q.push_back(s);
        incf[s] = INF;
        while(!q.empty()){     
            int u = q.front();q.pop_front();
            inq[u] = 0;
            for(int i=head[u];~i;i=edge[i].next){
                int v=edge[i].to,cap=edge[i].cap,cost=edge[i].cost;
                if(cap>0 && dist[v]>dist[u]+cost){     //更新每个点的最小费用
                    dist[v]=dist[u]+cost;
                    incf[v]=min(incf[u], cap);     //得到v点所在路径上的最小容量
                    pre[v]=i;     //记录下这个点的前驱正向边
                    if(!inq[v]){
                        inq[v]=1;
                        if(q.empty() || dist[v]>dist[q.front()])q.push_back(v);
                        else q.push_front(v);       //如果最小费用小于等于队首,直接塞入队列首
                    }
                }
            }
        }
        return dist[t]!=INF;
    }
    void update(int s,int t){
        int x=t;
        while(x!=s){
            int pos=pre[x];
            edge[pos].cap-=incf[t];      
            edge[pos^1].cap+=incf[t];
            x=edge[pos^1].to;
        }
        mxflow+=incf[t];
        mncost+=dist[t]*incf[t];
    }
    void minCostMaxFlow(int s,int t){
        while(spfa(s,t))
            update(s,t);
    }
}mcmf;

const int NN = 500 + 7;
struct Node{ int l, r, w, fp; } node[NN];

int st,ed;
inline void GetMap(){
    int n,m,k,w;
    read(n);read(m);read(k);read(w);
    st=0,ed=2*m+2;       //0代表源点,2*m+1代表慈元典,ed代表汇点
    mcmf.init(st,ed,st,ed);
    mcmf.add(st,2*m+1,k,0);      //源点向次源点连一条容量为k,费用为0的边
    for(int i=1;i<=m;i++)
        scanf("%d%d%d%d",&node[i].l,&node[i].r,&node[i].w,&node[i].fp);
    for(int i=1;i<=m;i++){
        for(int j=1;j<=m;j++){
            if(i==j)continue;
            if(node[i].r<=node[j].l)     //对于那些满足条件的video, i的出点向j的入点连一条容量为1,费用为0(或者如果这两个点类型相同的话,费用就为W,因为本题是将所有费用置为它的相反数了)
                mcmf.add(i+m,j,1,(node[i].fp==node[j].fp)?w:0);      //因为本题是求最大费用,所以要将所有费用取反,这里就是 w 
        }
    }
    for(int i=1;i<=m;i++)
        mcmf.add(i,i+m,1,-node[i].w);   //拆点,所有点的入点向入点连一条容量为1,费用为node[i].w的边
    for(int i=1;i<=m;i++)
        mcmf.add(2*m+1,i,1,0);     //源点向所有的点连上一条容量为1,费用为0的边
    for(int i=1;i<=m;i++)
        mcmf.add(i+m,ed,1,0);      //所有点的出度连一条容量为1,费用为0的边
}
inline void Solve(){
    mcmf.minCostMaxFlow(st,ed);
    printf("%d\n",-mcmf.mncost);     //取相反数,就是实际意义上的最大费用
}
int main(){
    int T;cin>>T;
    while(T--){
        GetMap();
        Solve();
    }
}

猜你喜欢

转载自www.cnblogs.com/00isok/p/10705047.html
今日推荐