POJ 3680 Intervals

9 篇文章 0 订阅
1 篇文章 0 订阅

题目在这里呀

哎失望地开始口糊题解了呀,bzoj2879 TLE调不出来,于是难过地开始整理AC的题了(ಥ﹏ಥ)...

这题很有想法的!

题意:有n个区间,每个区间有一个权值wi,从中取一些区间,使得任意整数点的被包含次数小于等于k,并且这些区间的权值和最大。

题解:这题和志愿者招募蜜汁相似啊ww~~

对于每个点列出式子。

设xi表示第i个区间有没有包含该点,xi=0 or 1

再引入一个余量ri

这样式子可以写成:

0+r0=k

x1+x3+x6+r1=k

x2+x5+x6+r2=k

......

0+rn+1=k


相邻两点相减,x2+x5+r2-x1-x3-r1=0

看见右端为0,可以想到一个点的入流=出流。

而相邻两个点相减多出来的一定是新产生的或者是新消失的。

那么就可以把问题转化成费用流啦!

区间的两个端点连边,流量为1,费用为-wi(否则不会去流)

相邻端点之间连边,流量为k,费用为0

建立源点和汇点与第一个和最后一个点连边。

跑费用流即可。

这题或许有点难理解(真的w

边数初始化1这个东西永远不能忘!!





#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#define N 1010
using namespace std;
int Q,n,k,cnt,tot,S,T,ans,mark[N],dis[N],head[N],cur[N],c[N],a[N],b[N],w[N];
map<int,int> id;
const int inf=1e9;
struct edge{
	int from,to,next,v,c;
}e[N];
inline int read(){
    int x=0,f=1;char ch=getchar();
    while (ch<'0' || ch>'9'){if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void add_edge(int x,int y,int v,int c){
	e[++cnt]=(edge){x,y,head[x],v,c};head[x]=cnt;
	e[++cnt]=(edge){y,x,head[y],0,-c};head[y]=cnt;
}
inline bool spfa(){
	memset(mark,0,sizeof(mark));
	for(int i=0;i<=T;i++) dis[i]=inf;
	queue<int>q;
	dis[S]=0;q.push(S);mark[S]=1;
	while(!q.empty()){
		int now=q.front();q.pop();
		for(int i=head[now];i!=-1;i=e[i].next){
			int v1=e[i].to;
			if(e[i].v && dis[v1]>dis[now]+e[i].c){
				dis[v1]=dis[now]+e[i].c;
				cur[v1]=i;
				if(!mark[v1]){q.push(v1);mark[v1]=1;}
			}
		}
		mark[now]=0;
	}
	return dis[T]!=inf;
}
inline void mcf(){
	ans=0;
	while(spfa()){
		int flow=inf;
		for(int i=cur[T];i;i=cur[e[i].from]) flow=min(flow,e[i].v);
		ans+=flow*dis[T];
		for(int i=cur[T];i;i=cur[e[i].from]){e[i].v-=flow;e[i ^ 1].v+=flow;}
	}
}
int main()
{
	Q=read();
	while(Q--){
		memset(head,-1,sizeof(head));
		memset(cur,0,sizeof(cur));
		memset(c,0,sizeof(c));
		n=read();k=read();
		for(int i=1;i<=n;i++){
			a[i]=read();b[i]=read();w[i]=read();
			c[i+i-1]=a[i];c[i+i]=b[i];
		}
		sort(c+1,c+n+n+1);
		tot=0;cnt=1;
		for(int i=1;i<=n+n;i++) if(i==1 || c[i] != c[i-1]) id[c[i]]=++tot;
		S=0;T=tot+1;
		for(int i=1;i<tot;i++) add_edge(i,i+1,k,0);
		add_edge(S,1,k,0);add_edge(tot,T,k,0);
		for(int i=1;i<=n;i++) add_edge(id[a[i]],id[b[i]],1,-w[i]);
		mcf();
		printf("%d\n",-ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值