zoukankan      html  css  js  c++  java
  • 王子 [费用流]

    题意

    给定$n,k,p,q$,现在要给一段长度为n的格子染黑白,第$i$个格子染成黑色得到$a_i$分,白色$b_i$分

    要求连续的$k$个格子中要有至少$p$个黑的,$q$和白的

    求最大得分

    思路

    一眼是没想法的......瞄了一眼题解看到“网络流”三个大字,就会了

    还是要多想一些方法,多试试,因为这种题看着不是dp就是什么鬼畜的转化,那转化模型肯定先看是不是网络流

    考虑序列模型的经典网络流转化方法

    本题中的核心限制是对于每一段$k$区间的颜色数限制

    可以看到实际上这个$p,q$就是限制黑色格子的数量是$[p,k-q]$

    同时这个限制对于每一个格子而言,这个格子参与$k$个区间的限制

    我们考虑这样一个方法:

    首先把所有的都涂成白色,然后用跨越一个$k$区间的一点流量来代表变成一个黑格子带来的影响,其他的从$i$到$i+1$的代表一个白格子

    那么这样我们把白格子边设成流量上限为$k-q-p$上限,费用为0,代表额外的白变黑的数量上限

    黑格子边显然容量为1,费用为对应的格子的$b_i-a_i$

    一些细节注意:

    首先这个跨越的黑格子边,从$i$出发,终点在$k+i$不是在$k+i-1$

    然后注意最开始的$k$个点,没有前驱去到达它们,那么建立一个辅助节点$aux$,从$s->aux$连一条容量为$k-q$的(限制一下),再从$aux$连向$1$到$k$号点

    实现上可以把费用流的$ans$初值设为所有白格子分数的和,然后用上面定义的费用直接算就可以了

    费用流推荐使用spfa魔改版zkw多路增广,不过本题中单路多路其实差距不大,$EK$还可能更快(因为图的形态问题)

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cassert>
    #include<queue>
    #define ll long long
    using namespace std;
    inline ll read(){
    	ll re=0,flag=1;char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-') flag=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    	return re*flag;
    }
    ll n,m,l,r,cnte=1,first[1010],dis[1010],vis[1010],ans=0,maxf=0;
    struct edge{
    	ll to,next,w,cap;
    }a[20010];
    inline void add(ll u,ll v,ll w,ll cap){
    	a[++cnte]=(edge){v,first[u],w,cap};first[u]=cnte;
    	a[++cnte]=(edge){u,first[v],-w,0,};first[v]=cnte;
    }
    queue<ll>q;
    bool spfa(ll s,ll t){
    	ll i,u,v;
    	for(i=s;i<=t+1;i++) dis[i]=-111111111111111ll,vis[i]=0;
    	vis[t]=1;dis[t]=0;q.push(t);
    	while(!q.empty()){
    		u=q.front();q.pop();vis[u]=0;
    		for(i=first[u];~i;i=a[i].next){
    			v=a[i].to;
    			if(!a[i^1].cap) continue;
    			if(dis[v]!=-111111111111111ll&&(dis[v]>=dis[u]+a[i^1].w)) continue;
    			dis[v]=dis[u]+a[i^1].w;
    			if(!vis[v]){
    				vis[v]=1;
    				q.push(v);
    			}
    		}
    	}
    	return dis[s]!=-111111111111111ll;
    }
    ll dfs(ll u,ll t,ll lim){
    	if(u==t||!lim) return lim;
    	ll i,v,f,flow=0;vis[u]=1;
    	for(i=first[u];~i;i=a[i].next){
    		v=a[i].to;if(vis[v]) continue;
    		if(dis[v]==dis[u]-a[i].w&&(f=dfs(v,t,min(lim,a[i].cap)))){
    			a[i].cap-=f;
    			a[i^1].cap+=f;
    			flow+=f;
    			lim-=f;
    			ans+=f*a[i].w;
    			if(!lim){vis[u]=0;return flow;}
    		}
    	}
    	dis[u]=-111111111111111ll;
    	return flow;
    }
    void mcmf(ll s,ll t){
    	while(spfa(s,t)){
    		memset(vis,0,sizeof(vis));
    		vis[t]=1;
    		while(vis[t]){
    			memset(vis,0,sizeof(vis));
    			maxf+=dfs(s,t,1e9);
    		}
    	}
    }
    ll x[1010],y[1010];
    int main(){
    	memset(first,-1,sizeof(first));
    	n=read();m=read();l=read();r=m-read();ll i;
    	for(i=1;i<=n;i++) x[i]=read(),y[i]=read(),ans+=y[i];
    	add(0,n+2,0,r);
    	for(i=1;i<=m;i++) add(n+2,i,0,1);
    	for(i=1;i<=n;i++){
    		add(i,i+1,0,r-l);
    		add(i,((i<=n-m)?i+m:n+1),x[i]-y[i],1);
    	}
    	mcmf(0,n+1);
    	cout<<ans<<'
    ';
    }
    
  • 相关阅读:
    (4.3)ODBC/OLE DB/ADO概念与使用情况
    查找至少连续出现三次的所有数字/连续3天的日期【LeetCode】
    javascript 事件触发
    document.referrer
    创业公司
    关于javascript 回调函数
    promise
    javascript deferred
    document.readystate
    window---->load, document------DOMContentLoaded
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/10023234.html
Copyright © 2011-2022 走看看