HDU-6437 Problem L.Videos(最大费用最大流&拆点)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kuronekonano/article/details/81975730

Problem Description
C-bacteria takes charge of two kinds of videos: ’The Collection of Silly Games’ and ’The Collection of Horrible Games’.
For simplicity’s sake, they will be called as videoA and videoB.
There are some people who want to watch videos during today, and they will be happy after watching videos of C-bacteria.
There are n hours a day, m videos are going to be show, and the number of people is K.
Every video has a type(videoA or videoB), a running time, and the degree of happi- ness after someone watching whole of it.
People can watch videos continuous(If one video is running on 2pm to 3pm and another is 3pm to 5pm, people can watch both of them).
But each video only allows one person for watching.
For a single person, it’s better to watch two kinds to videos alternately, or he will lose W happiness.
For example, if the order of video is ’videoA, videoB, videoA, videoB, …’ or ’B, A, B, A, B, …’, he won’t lose happiness; But if the order of video is ’A, B, B, B, A, B, A, A’, he will lose 3W happiness.
Now you have to help people to maximization the sum of the degree of happiness.

Input
Multiple query.
On the first line, there is a positive integer T, which describe the number of data. Next there are T groups of data.
for each group, the first line have four positive integers n, m, K, W : n hours a day, m videos, K people, lose W happiness when watching same videos).
and then, the next m line will describe m videos, four positive integers each line S, T, w, op : video is the begin at S and end at T, the happiness that people can get is w, and op describe it’s tpye(op=0 for videoA and op=1 for videoB).
There is a blank line before each groups of data.
T<=20, n<=200, m<=200, K < = 200, W<=20, 1<=S < T<=n, W<=w<=1000,
op=0 or op=1

Output
Your output should include T lines, for each line, output the maximum happiness for the corresponding datum.

Sample Input
2
10 3 1 10
1 5 1000 0
5 10 1000 1
3 9 10 0
10 3 1 10
1 5 1000 0
5 10 1000 0
3 9 10 0

Sample Output
2000
1990

题意:每天有n小时,m个视频,k个人,一个人只能看一个视频,每个视频有开始时间和结束时间以及种类A和种类B,不能同时看两个视频。每个人看视频将得到一个快乐值val,如果交替看不同种类的视频如ABABA那么不会减小快乐值,否则看一次与之前重复的视频就减少W的快乐值。如AABBAAA则减少4次快乐值。即4W。问最大化的快乐值是多少。

MLS一开始就想到了用网络流建图。而且建的非常神奇。

首先有一个超级源点和超级汇点,连到起点k的容量,汇点到超级汇点也是如此。因为一个视频有开始时间和结束时间,为了表示时间对看视频先后顺序的限制,将一个视频拆成两个节点,即开始时间一个节点,结束时间一个节点,该边费用为获得的快乐值。容量为1,表示每个人能看一个视频。

接着判断两两视频间的关系,首先是视频 i 结束时间小于等于视频 j 的开始时间才能连一条有向边,接着判断两视频的种类,如果种类相同则边权为-W,否则为0。

最后直接跑一个最大费用最大流即可。即最小费用最大流的相反数,把所有边权取相反数,结果取相反数即可。

更新:超级源点和超级汇点只是为了限制人数K,如果没有K的限制,那么答案只在人数小于等于视频数量的时候是对的,否则视频数量多于人数的时候就会跑超人数。再者。超级源点超级汇点只用存在一个即可,都能限制人数,因此不必要同时存在。

#include<bits/stdc++.h>///最大费用最大流,将边权变成相反数即可
#define LL long long
#define M(a,b) memset(a,b,sizeof a)
#define pb(x) push_back(x)
using namespace std;
const int maxn=1e5+7;
const int INF=0x3f3f3f3f;
struct edge
{
    int to,next,cap,flow,cost;
    edge() {}
    edge(int a,int b,int c,int d,int f)
    {
        to=a;
        cap=b;
        flow=c;
        cost=d;
        next=f;
    }
} mp[maxn<<1];
int pre[maxn],dis[maxn];
bool vis[maxn];
int head[maxn];
int n,m,k,w,cnt,N;
void init(int n)
{
    N=n;
    cnt=0;
    M(head,-1);
}
void addedge(int from,int to,int cap,int cost)
{
    mp[cnt]=edge(to,cap,0,cost,head[from]);
    head[from]=cnt++;
    mp[cnt]=edge(from,0,0,-cost,head[to]);
    head[to]=cnt++;
}
bool spfa(int s,int t)
{
    queue<int>q;
    for(int i = 0; i <=N; i++)
    {
        dis[i] = INF;
        vis[i] = false;
        pre[i] = -1;
    }
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = mp[i].next)
        {
            int v = mp[i].to;
            if(mp[i].cap > mp[i].flow && dis[v] > dis[u] + mp[i].cost )
            {
                dis[v] = dis[u] + mp[i].cost;
                pre[v] = i;
                if(!vis[v])
                {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t] == -1)return false;
    else return true;
}
int minCostMaxflow(int s,int t,int &cost)
{
    int flow = 0;
    cost = 0;
    while(spfa(s,t))
    {
        int Min = INF;
        for(int i = pre[t]; i != -1; i = pre[mp[i^1].to])
        {
            if(Min > mp[i].cap - mp[i].flow)
                Min = mp[i].cap - mp[i].flow;
        }
        for(int i = pre[t]; i != -1; i = pre[mp[i^1].to])
        {
            mp[i].flow += Min;
            mp[i^1].flow -= Min;
            cost += mp[i].cost * Min;
        }
        flow += Min;
    }
    return flow;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d%d",&n,&m,&k,&w);
        init(m<<1+4);
        int from[maxn],to[maxn],val,flag[maxn];
        int st=0,ed=(m<<1)+1,S=(m<<1)+2,T=(m<<1)+3;///因此每个视频被拆成两个节点,因此总结点数达到了m*2,再加上超级源点和超级汇点
        addedge(st,S,k,0);
        addedge(T,ed,k,0);
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d%d",&from[i],&to[i],&val,&flag[i]);
            addedge(S,i,1,0);
            addedge(i,i+m,1,-val);///因为有时间的限制,因此将一个视频拆成开始时间和结束时间
            addedge(i+m,T,1,0);
            for(int j=1; j<i; j++)///在输入的同时比较其他视频的开始时间,将结束时间小于开始时间的建一条有向边,容量为1,一个人只能看一个视频
                if(to[i]<=from[j])///再判断是否是同种视频,是同种则边权为负,否则为0
                    addedge(i+m,j,1,flag[i]==flag[j]?w:0);
                else if(to[j]<=from[i])
                    addedge(j+m,i,1,flag[i]==flag[j]?w:0);
        }
        int ans=0;
        minCostMaxflow(st,ed,ans);
        printf("%d\n",-ans);
    }
}

猜你喜欢

转载自blog.csdn.net/kuronekonano/article/details/81975730
今日推荐