• POJ 3680 Intervals


    POJ 3680 Intervals

    题目链接

    题目大意:给定一些开区间((a_i,b_i)),以及区间的价值(w_i)。每个点最多被(k)个区间覆盖,要求最大化选取的区间的价值和。

    非常神奇的费用流。

    我们首先将点离散化,假设有(m)个点。然后建立超级源(S)和超级汇(T)

    首先连两条边((S,1,k,0),(m,T,k,0)),然后相邻两点之间连边((i,i+1,k,0))。然后对于区间((a,b)),我们连((a,b,1,-w))。求出最小费用流后取反。

    要想搞懂这神奇的建边方式就要先搞懂它是如何满足“每个点最多被(k)个区间覆盖”这一限制的。在一个网络中,流入点(i)的流量(flow)就是(i)还可以被覆盖的次数,或者说(i)在之前被覆盖了(k-flow)次。当我们选择了区间((a,b))后,((a,b))这些节点的剩余流量都减少了,于是我们连((a,b,1,-w)),将这部分流量分流出去。

    这种带反悔的最优化问题就可以用费用流来高效解决。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    
    #define ll long long
    #define N 405
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int n,k;
    int cas;
    struct interval {
    	int x,y,w;
    }e[N];
    int d[N<<1];
    struct load {
    	int to,next;
    	int flow,c;
    }s[N<<2];
    int h[N],cnt;
    int S,T;
    void add(int i,int j,int flow,int c) {
    	s[++cnt]=(load) {j,h[i],flow,c};h[i]=cnt;
    	s[++cnt]=(load) {i,h[j],0,-c};h[j]=cnt;
    }
    int dis[N];
    queue<int>q;
    bool in[N];
    int fr[N],edge[N];
    int ans;
    bool spfa() {
    	memset(dis,0x3f,sizeof(dis));
    	dis[0]=0;
    	q.push(0);
    	while(!q.empty()) {
    		int v=q.front();q.pop();
    		in[v]=0;
    		for(int i=h[v];i;i=s[i].next) {
    			int to=s[i].to;
    			if(s[i].flow&&dis[to]>dis[v]+s[i].c) {
    				dis[to]=dis[v]+s[i].c;
    				fr[to]=v;
    				edge[to]=i;
    				if(!in[to]) {
    					in[to]=1;
    					q.push(to);
    				}
    			}
    		}
    	}
    	if(dis[T]>1e9) return 0;
    	int mn=1e9;
    	for(int i=T;i;i=fr[i]) mn=min(mn,s[edge[i]].flow);
    	for(int i=T;i;i=fr[i]) s[edge[i]].flow-=mn,s[edge[i]^1].flow+=mn;
    	ans+=mn*dis[T];
    	return 1;
    }
    int main() {
    	cas=Get();
    	while(cas--) {
    		d[0]=0;
    		n=Get(),k=Get();
    		for(int i=1;i<=n;i++) {
    			int a=Get(),b=Get(),c=Get();
    			e[i]=(interval) {a,b,c};
    			d[++d[0]]=a;
    			d[++d[0]]=b;
    		}
    		sort(d+1,d+1+d[0]);
    		int cc=unique(d+1,d+1+d[0])-d;
    		for(int i=1;i<=n;i++) {
    			e[i].x=lower_bound(d+1,d+cc,e[i].x)-d;
    			e[i].y=lower_bound(d+1,d+cc,e[i].y)-d;
    		}
    		T=cc;
    		memset(h,0,sizeof(h));
    		cnt=1;
    		add(S,1,k,0),add(T-1,T,k,0);
    		for(int i=1;i<T-1;i++) add(i,i+1,k,0);
    		for(int i=1;i<=n;i++) {
    			add(e[i].x,e[i].y,1,-e[i].w);
    		}
    		ans=0;
    		while(spfa());
    		cout<<-ans<<"
    ";
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    mixin混合
    python类内部调用自己的成员函数必须加self
    合并/取消合并单元格
    pandas 显示所有的行和列
    pandas 利用openpyxl设置表格样式
    Nowcoder9983G.糖果(并查集)
    Nowcoder9983F.匹配串(思维)
    Nowcoder9983E.买礼物(链表+线段树)
    Nowcoder9983C.重力追击(暴力+少用sqrt)
    Nowcoder9983B.内卷(双指针)
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10073185.html
Copyright © 2020-2023  润新知