大致题意:
给你n个开区间,每个区间都有一个权值,现在从中选出一些区间使得所选区间的总权值最大,且所有区间上的数字被覆盖次数不能超过k次。
解题思路:
这是挑战书上的一道例题(p246),是一道费用流的题,不看题解我是想不到这道题如何构图的。
这道题的构图思想大致如下:
因为区间区间端点值很大,但是n很小,所以我们可以将区间离散化。离散化之后,端点有2*n个,大约最多400个。
我们将这些点排序,然后从前往后将第i个点和第i+1各点相连,设容量为无穷(这条边可以走多次,且可从这些边流向其他边),费用为0(对结果没影响)。然后按照给出的区间,将每个区间的端点进行连接,设容量为1(每个区间最多能取一次),费用设成-w(因为要求最大权值,我们将权值取反,求一次最小费用最大流,然后再将权值取反即是最大权值)。
最后,将1连向源点,容量为k(从源点开始流,因为其他边的流量为1,那么从源点最多可流k,即有k个增广路径,即使有的区间相交了,那么相交(覆盖)这部分最多被覆盖k次,所以这是可行的),费用为0,再将终点连向汇点,容量为k,费用为0。然后跑一边费用流即可。
如图所示,假设0是汇点在1之间有一条流量为k,费用为0的边,源点为5,在4之间有一条流量为k,费用为0的边。
假设k为1的时候,从原点到汇点只能走一条,按照费用流思想,会选择最大权值,既0-(-4-8)=12。
假设k为2的时候,先选2->3->4,再选1->3->4(3->4走的是无穷流边,因为流量为1的边已经走过了,没流了),答案为0-(-2-4-8)=14.
我这是简单说明一下,这样构图会保证每个子区间被覆盖的次数不超过k次且费用最大,但并没有体现费用流中加反向弧的精髓。
代码:
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
#include<vector>
#include<iostream>
using namespace std;
#define maxn 500
#define inf 0x3f3f3f3f
struct Lu
{
int to;
int flow;
int cost;
Lu(int to=0,int flow=0,int cost=0):to(to),flow(flow),cost(cost) {}
};
vector<Lu>E;
vector<int>V[maxn];
int a[maxn];
int x[maxn];
int y[maxn];
int w[maxn];
int dis[maxn];
int vis[maxn];
int pre[maxn];
int spfa(int s,int t)
{
for(int i=s; i<=t; i++)
{
pre[i]=-1;
vis[i]=0;
dis[i]=inf;
}
queue<int>Q;
dis[s]=0;
vis[s]=1;
Q.push(s);
int top,vlen;
Lu L;
while(!Q.empty())
{
top=Q.front();
vis[top]=0;
Q.pop();
vlen=V[top].size();
for(int i=0; i<vlen; i++)
{
L=E[V[top][i]];
if(L.flow>0&&dis[L.to]>dis[top]+L.cost)
{
dis[L.to]=dis[top]+L.cost;
pre[L.to]=V[top][i];
if(!vis[L.to])
{
vis[L.to]=1;
Q.push(L.to);
}
}
}
}
if(dis[t]==inf)return 0;
return 1;
}
int MinCostMaxFlow(int s,int t)
{
int cost=0,minl=inf;
while(spfa(s,t))
{
minl=inf;
for(int i=pre[t]; i!=-1; i=pre[E[i^1].to])
{
//cout<<"+++"<<endl;
minl=min(minl,E[i].flow);
}
for(int i=pre[t]; i!=-1; i=pre[E[i^1].to])
{
E[i].flow-=minl;
E[i^1].flow+=minl;
cost+=minl*E[i].cost;
}
}
return 0-cost;
}
int main()
{
int t,n,k,cnt;
scanf("%d",&t);
while(t--)
{
cnt=0;
scanf("%d%d",&n,&k);
for(int i=0; i<=2*n+2; i++)V[i].clear();
E.clear();
for(int i=1; i<=n; i++)
{
scanf("%d%d%d",&x[i],&y[i],&w[i]);
a[++cnt]=x[i];
a[++cnt]=y[i];
}
sort(a+1,a+cnt+1);
cnt=unique(a+1,a+cnt+1)-(a+1);
//printf("%++d\n",cnt);
for(int i=1; i<=n; i++)
{
x[i]=upper_bound(a+1,a+cnt+1,x[i])-(a+1);
y[i]=upper_bound(a+1,a+cnt+1,y[i])-(a+1);
//printf("%d %d\n",x[i],y[i]);
E.push_back(Lu(y[i],1,0-w[i]));
E.push_back(Lu(x[i],0,w[i]));
V[y[i]].push_back(E.size()-1);
V[x[i]].push_back(E.size()-2);
}
for(int i=1; i<cnt; i++)
{
E.push_back(Lu(i+1,inf,0));
E.push_back(Lu(i,0,0));
V[i+1].push_back(E.size()-1);
V[i].push_back(E.size()-2);
}
E.push_back(Lu(1,k,0));
E.push_back(Lu(0,0,0));
V[1].push_back(E.size()-1);
V[0].push_back(E.size()-2);
E.push_back(Lu(cnt+1,k,0));
E.push_back(Lu(cnt,0,0));
V[cnt+1].push_back(E.size()-1);
V[cnt].push_back(E.size()-2);
int cost=MinCostMaxFlow(0,cnt+1);
printf("%d\n",cost);
}
}
/*
4
5 1
78 10000 1
78 100 1
79 10001 1
500 600 1
450 550 1
*/