网络流解决k次区间覆盖问题

前两题焦作赛区的网络预选赛上,出现了一个k次覆盖区间问题。当时第一想法是线段树优化建图,但是也不会这个,所以当时就放过去了。赛后补题的时候发现这题可以通过网络流直接建模解决。

首先我们来看一个例题

Intervals

 POJ - 3680 

You are given N weighted open intervals. The ith interval covers (aibi) and weighs wi. Your task is to pick some of the intervals to maximize the total weights under the limit that no point in the real axis is covered more than k times.

Input

The first line of input is the number of test case.
The first line of each test case contains two integers, N and K (1 ≤ K ≤ N ≤ 200).
The next N line each contain three integers aibiwi(1 ≤ ai < bi ≤ 100,000, 1 ≤ wi ≤ 100,000) describing the intervals. 
There is a blank line before each test case.

Output

For each test case output the maximum total weights in a separate line.

Sample Input

4

3 1
1 2 2
2 3 4
3 4 8

3 1
1 3 2
2 3 4
3 4 8

3 1
1 100000 100000
1 2 3
100 200 300

3 2
1 100000 100000
1 150 301
100 200 300

Sample Output

14
12
100000
100301

题意意思是给出n个开区间,区间最多可以选一次,且所有的区间都有一定的权值,与此同时,要保证实轴上的所有数被选择的次数要小于k次。问如何选出最大的权值。

扫描二维码关注公众号,回复: 3463749 查看本文章

很容易看出这是个费用流的题目,但是到底应该怎么建模可以保证每个点最多被选k次且每个区间最多选一次?

简单地想一下,我们可以每个区间建立一个虚点,超级源点向虚点连接一条容量为区间长度,价值为这个区间价值的负边,然后这个点向区间内所有的点连一条容量为1,价值为0 的边,然后所有的点向超级汇点连接一条容量为1的价值为0 的点。但是这个实际上并不能保证一个区间被选中之后,其中的所有的点都被选中。

换一个建模方法,设置一个超级源点,一个超级汇点,源点向第一个点连一条容量为k,费用为0的边。再将所有区间内的数之间连上用容量为k费用为0 的边连接上再将最后一个点与汇点用一条容量为k的费用为0 的边,再在区间的两个端点之间连接一条容量为1的,费用为-w的边,形成这样一种结构,如下图:

这样保证每个点最多被经过k次,,且一段选中了这条边之间的端点就会被跳过。需要注意的是这样的右端点是不在端点内的,因为这样右端点依然会被经过。

AC代码:

#include<cstring>
#include<cstdio> 
#include<queue>
#include<vector>
#include<iostream>
#include<algorithm>
typedef int Int;
#define int long long
using namespace std;
const int maxn=505;
const int INF=0x3f3f3f3f;
struct Edge{
	int from,to,cap,flow,cost;
	Edge(){}
	Edge(int from,int to,int cap,int flow,int cost):from(from),to(to),cap(cap),flow(flow),cost(cost){}
};
struct MCMF
{
    int n,m,s,t;
    vector<Edge> edges;
    vector<int> g[maxn];
    int inq[maxn];
    int d[maxn];
    int p[maxn];
    int a[maxn];

    void init(int n)
    {
        this->n =n;
        for(int i=0; i<n; i++)g[i].clear();
        edges.clear();
    }
    void AddEdge(int from,int to,int cap,int cost)
    {
        Edge e1= Edge(from,to,cap,0,cost), e2= Edge(to,from,0,0,-cost);
        edges.push_back(e1);
        edges.push_back(e2);
        m=edges.size();
        g[from].push_back(m-2);
        g[to].push_back(m-1);
    }
    bool spfa(int s,int t, int & flow,int & cost)
    {
        for(int i=0; i<=n; i++)
            d[i]=INF;
        memset(inq,0,sizeof(inq));
        d[s]=0;
        inq[s]=1;
        p[s]=0;
        a[s]=INF;
        queue<int>q;
        q.push(s);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            inq[u]=0;
            for(int i=0; i<g[u].size(); i++)
            {
                Edge & e = edges[g[u][i]];
                if(e.cap>e.flow && d[e.to]>d[u]+e.cost)
                {
                    d[e.to]=d[u]+e.cost;
                    p[e.to]=g[u][i];
                    a[e.to]=min(a[u],e.cap-e.flow);
                    if(!inq[e.to])
                    {
                        q.push(e.to);
                        inq[e.to]=1;
                    }
                }
            }
        }
        if(d[t]==INF||d[t]>0)
            return false;

        flow+=a[t];
        cost+=a[t]*d[t];
        for(int u=t; u!=s; u=edges[p[u]].from)
        {
            edges[p[u]].flow +=a[t];
            edges[p[u]^1].flow-=a[t];
        }
        return true;
    }

    int  Mincost(int s,int t)
    {
        int flow=0,cost =0;
        while(spfa(s,t,flow,cost)) ;
        return cost;
    }
};
int u[205],v[205],w[205];
int uid[205],vid[205];
Int main()
{
	int t;
	scanf("%lld",&t);
	while(t--)
	{
		int n,k,m;
		vector<int> V;
		scanf("%lld%lld",&m,&k);
		for(int i=1;i<=m;i++)
		{
			scanf("%lld%lld%lld",&u[i],&v[i],&w[i]);
			V.push_back(u[i]);
			V.push_back(v[i]);
		}
		sort(V.begin(),V.end());
		V.erase(unique(V.begin(),V.end()),V.end());
		int cnt=V.size();
		for(int i=1;i<=m;i++) uid[i]=lower_bound(V.begin(),V.end(),u[i])-V.begin()+1;
		for(int i=1;i<=m;i++) vid[i]=lower_bound(V.begin(),V.end(),v[i])-V.begin()+1;
		MCMF pro;
		pro.init(cnt+10);
		int ss=0,tt=cnt+1;
		pro.AddEdge(ss,1,k,0);
		pro.AddEdge(cnt,tt,k,0);
		for(int i=1;i<=cnt-1;i++)
		{
			pro.AddEdge(i,i+1,INF,0);
		}
		for(int i=1;i<=m;i++)
		{
			//cout<<u[i]<<' '<<v[i]<<' '<<uid[i]<<' '<<vid[i]<<endl;
			pro.AddEdge(uid[i],vid[i],1,-w[i]);
		}
		printf("%lld\n",-pro.Mincost(ss,tt));
	}
}

然后再来看网络赛上这题:

Modular Production Line 题库链接

An automobile factory has a car production line. Now the market is oversupply and the production line is often shut down. To make full use of resources, the manager divides the entire production line into NN parts (1...N)(1...N). Some continuous parts can produce sub-products. And each of sub-products has their own value. The manager will use spare time to produce sub-products to make money. Because of the limited spare time, each part of the production line could only work at most KK times. And Because of the limited materials, each of the sub-products could be produced only once. The manager wants to know the maximum value could he make by produce sub-products.

Input

The first line of input is TT, the number of test case.

The first line of each test case contains three integers, N, KN,K and MM. (MM is the number of different sub-product).

The next MM lines each contain three integers A_i, B_i, W_iAi​,Bi​,Wi​ describing a sub-product. The sub-product has value W_iWi​. Only A_iAi​ to B_iBi​ parts work simultaneously will the sub-product be produced (include A_iAi​ to B_iBi​).

1 \le T \le 1001≤T≤100

1 \le K \le M \le 2001≤K≤M≤200

1 \le N \le 10^51≤N≤105

1 \le A_i \le B_i \le N1≤Ai​≤Bi​≤N

1 \le W_i \le 10^51≤Wi​≤105

Output

For each test case output the maximum value in a separate line.

样例输入复制

4
10 1 3
1 2 2
2 3 4
3 4 8
10 1 3
1 3 2
2 3 4
3 4 8
100000 1 3
1 100000 100000
1 2 3
100 200 300
100000 2 3
1 100000 100000
1 150 301
100 200 300

样例输出复制

10
8
100000
100301

题目来源

ACM-ICPC 2018 焦作赛区网络预赛

!!!woc这两个题目居然完全一样。但是仔细一看发现不对,这题是一个闭区间。所以右端点需要跳过所以是u向v+1连边。

#include<cstring>
#include<queue>
#include<vector>
#include<iostream>
#include<algorithm>
typedef int Int;
#define int long long
using namespace std;
const int maxn=505;
const int INF=0x3f3f3f3f;
struct Edge{
	int from,to,cap,flow,cost;
	Edge(){}
	Edge(int from,int to,int cap,int flow,int cost):from(from),to(to),cap(cap),flow(flow),cost(cost){}
};
struct MCMF
{
    int n,m,s,t;
    vector<Edge> edges;
    vector<int> g[maxn];
    int inq[maxn];
    int d[maxn];
    int p[maxn];
    int a[maxn];

    void init(int n)
    {
        this->n =n;
        for(int i=0; i<n; i++)g[i].clear();
        edges.clear();
    }
    void AddEdge(int from,int to,int cap,int cost)
    {
        Edge e1= Edge(from,to,cap,0,cost), e2= Edge(to,from,0,0,-cost);
        edges.push_back(e1);
        edges.push_back(e2);
        m=edges.size();
        g[from].push_back(m-2);
        g[to].push_back(m-1);
    }
    bool spfa(int s,int t, int & flow,int & cost)
    {
        for(int i=0; i<=n; i++)
            d[i]=INF;
        memset(inq,0,sizeof(inq));
        d[s]=0;
        inq[s]=1;
        p[s]=0;
        a[s]=INF;
        queue<int>q;
        q.push(s);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            inq[u]=0;
            for(int i=0; i<g[u].size(); i++)
            {
                Edge & e = edges[g[u][i]];
                if(e.cap>e.flow && d[e.to]>d[u]+e.cost)
                {
                    d[e.to]=d[u]+e.cost;
                    p[e.to]=g[u][i];
                    a[e.to]=min(a[u],e.cap-e.flow);
                    if(!inq[e.to])
                    {
                        q.push(e.to);
                        inq[e.to]=1;
                    }
                }
            }
        }
        if(d[t]==INF||d[t]>0)
            return false;

        flow+=a[t];
        cost+=a[t]*d[t];
        for(int u=t; u!=s; u=edges[p[u]].from)
        {
            edges[p[u]].flow +=a[t];
            edges[p[u]^1].flow-=a[t];
        }
        return true;
    }

    int  Mincost(int s,int t)
    {
        int flow=0,cost =0;
        while(spfa(s,t,flow,cost)) ;
        return cost;
    }
};
int u[205],v[205],w[205];
int uid[205],vid[205];
Int main()
{
	int t;
	scanf("%lld",&t);
	while(t--)
	{
		int n,k,m;
		vector<int> V;
		scanf("%lld%lld%lld",&n,&k,&m);
		for(int i=1;i<=m;i++)
		{
			scanf("%lld%lld%lld",&u[i],&v[i],&w[i]);
			V.push_back(u[i]);
			V.push_back(v[i]);
		}
		sort(V.begin(),V.end());
		V.erase(unique(V.begin(),V.end()),V.end());
		int cnt=V.size();
		for(int i=1;i<=m;i++) uid[i]=lower_bound(V.begin(),V.end(),u[i])-V.begin()+1;
		for(int i=1;i<=m;i++) vid[i]=lower_bound(V.begin(),V.end(),v[i])-V.begin()+1;
		MCMF pro;
		pro.init(cnt+10);
		int ss=0,tt=cnt+1;
		pro.AddEdge(ss,1,k,0);
		pro.AddEdge(cnt,tt,k,0);
		for(int i=1;i<=cnt-1;i++)
		{
			pro.AddEdge(i,i+1,INF,0);
		}
		for(int i=1;i<=m;i++)
		{
			//cout<<u[i]<<' '<<v[i]<<' '<<uid[i]<<' '<<vid[i]<<endl;
			pro.AddEdge(uid[i],vid[i]+1,1,-w[i]);
		}
		printf("%lld\n",-pro.Mincost(ss,tt));
	}
}

再来看一个

The Bonus Salary!

 POJ - 3762 

In order to encourage employees' productivity, ACM Company has made a new policy. At the beginning of a period, they give a list of tasks to each employee. In this list, each task is assigned a "productivity score". After the first K days, the employee who gets the highest score will be awarded bonus salary.

Due to the difficulty of tasks, for task i-th:

  • It must be done from hh_Li mm_Li : ss_Li to hh_Ri : mm_Ri : ss_Ri.
  • This range of time is estimated very strictly so that anyone must use all of this time to finish the task.

Moreover, at a moment, each employee can only do at most one task. And as soon as he finishes a task, he can start doing another one immediately.

XYY is very hard-working. Unfortunately, he's never got the award. Thus, he asks you for some optimal strategy. That means, with a given list of tasks, which tasks he should do in the first K days to maximize the total productivity score. Notice that one task can be done at most once.

Input

The first line contains 2 integers N and K (1 ≤ N ≤ 2000, 0 ≤ K ≤ 100), indicating the number of tasks and days respectively. This is followed by N lines; each line has the following format:

hh_Li:mm_Li:ss_Li hh_Ri:mm_Ri:ss_Ri w

Which means, the i-th task must be done from hh_Li mm_Li : ss_Li to hh_Ri : mm_Ri : ss_Ri and its productivity score is w. (0 ≤hh_Lihh_Ri ≤ 23, 0 ≤mm_Limm_Riss_Liss_Ri ≤ 59, 1 ≤ w ≤ 10000). We use exactly 2 digits (possibly with a leading zero) to represent hhmm and ss. It is guaranteed that the moment hh_Ri : mm_Ri : ss_Ri is strictly later than hh_Li mm_Li : ss_Li. 

Output

The output only contains a nonnegative integer --- the maximum total productivity score.

Sample Input

5 2
09:00:00 09:30:00 2
09:40:00 10:00:00 3
09:29:00 09:59:00 10
09:30:00 23:59:59 4
07:00:00 09:31:00 3

Sample Output

16

Hint

The optimal strategy is:
Day1: Task1, Task 4
Day2: Task 3
The total productivity score is 2 + 4 + 10 = 16.

本题同理,且区间的两个端点都不会覆盖到。

#include<cstring>
#include<cstdio> 
#include<queue>
#include<vector>
#include<iostream>
#include<algorithm>
typedef int Int;
using namespace std;
const int maxn=5050;
const int INF=0x3f3f3f3f;
struct Edge{
	int from,to,cap,flow,cost;
	Edge(){}
	Edge(int from,int to,int cap,int flow,int cost):from(from),to(to),cap(cap),flow(flow),cost(cost){}
};
struct MCMF
{
    int n,m,s,t;
    vector<Edge> edges;
    vector<int> g[maxn];
    int inq[maxn];
    int d[maxn];
    int p[maxn];
    int a[maxn];

    void init(int n)
    {
        this->n =n;
        for(int i=0; i<n; i++)g[i].clear();
        edges.clear();
    }
    void AddEdge(int from,int to,int cap,int cost)
    {
        Edge e1= Edge(from,to,cap,0,cost), e2= Edge(to,from,0,0,-cost);
        edges.push_back(e1);
        edges.push_back(e2);
        m=edges.size();
        g[from].push_back(m-2);
        g[to].push_back(m-1);
    }
    bool spfa(int s,int t, int & flow,int & cost)
    {
        for(int i=0; i<=n; i++)
            d[i]=INF;
        memset(inq,0,sizeof(inq));
        d[s]=0;
        inq[s]=1;
        p[s]=0;
        a[s]=INF;
        queue<int>q;
        q.push(s);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            inq[u]=0;
            for(int i=0; i<g[u].size(); i++)
            {
                Edge & e = edges[g[u][i]];
                if(e.cap>e.flow && d[e.to]>d[u]+e.cost)
                {
                    d[e.to]=d[u]+e.cost;
                    p[e.to]=g[u][i];
                    a[e.to]=min(a[u],e.cap-e.flow);
                    if(!inq[e.to])
                    {
                        q.push(e.to);
                        inq[e.to]=1;
                    }
                }
            }
        }
        if(d[t]==INF||d[t]>0)
            return false;

        flow+=a[t];
        cost+=a[t]*d[t];
        for(int u=t; u!=s; u=edges[p[u]].from)
        {
            edges[p[u]].flow +=a[t];
            edges[p[u]^1].flow-=a[t];
        }
        return true;
    }

    int  Mincost(int s,int t)
    {
        int flow=0,cost =0;
        while(spfa(s,t,flow,cost)) ;
        return cost;
    }
};
Int main()
{
	int n,k;
	scanf("%d%d",&n,&k);
	vector<int> V;
	V.clear();
	int l[2005],r[2005],w[2005];
	int lid[2005],rid[2005];
	for(Int i=1;i<=n;i++)
	{
		int lh,lm,ls,rh,rm,rs;
		scanf("%d:%d:%d %d:%d:%d %d",&lh,&lm,&ls,&rh,&rm,&rs,&w[i]);
		l[i]=lh*3600+lm*60+ls;
		r[i]=rh*3600+rm*60+rs;
		V.push_back(lh*3600+lm*60+ls);
		V.push_back(rh*3600+rm*60+rs);
	}
	sort(V.begin(),V.end());
	V.erase(unique(V.begin(),V.end()),V.end());
	int cnt=V.size();


	for(int i=1;i<=n;i++)
	{
		lid[i]=lower_bound(V.begin(),V.end(),l[i])-V.begin()+1;
		rid[i]=lower_bound(V.begin(),V.end(),r[i])-V.begin()+1;
	}
	MCMF pro;
	pro.init(cnt+10);
		int ss=0,tt=cnt+1;
		pro.AddEdge(ss,1,k,0);
		pro.AddEdge(cnt,tt,k,0);
		for(int i=1;i<=cnt-1;i++)
		{
			pro.AddEdge(i,i+1,INF,0);
		}
		for(int i=1;i<=n;i++)
		{
			//cout<<u[i]<<' '<<v[i]<<' '<<uid[i]<<' '<<vid[i]<<endl;
			pro.AddEdge(lid[i],rid[i],1,-w[i]);
		}
	printf("%d\n",-pro.Mincost(ss,tt));
}

猜你喜欢

转载自blog.csdn.net/baiyifeifei/article/details/82745762