Gym - 101190D[最大费用流]

题目链接:https://vjudge.net/problem/Gym-101190D

 

解题思路:

假设我们默认每个小时都是在吃,那么初始ans = ∑e[i],然后我将si变为si - ei,表示在i时刻选择睡觉时会获得si的收益。但是在k个连续的段中选择si的次数ms <= times <= k-me。

当ms == 0:

将问题转化为覆盖问题,那么每个点最多只能被覆盖k-me次,如果在i点选择了睡觉,那么就会将[i,i+k-1]的点都覆盖。这个问题跟我之前做的一题一模一样:点击这里

当ms>=0:

源点S = 0,汇点T = n + 1

对于i = 1,2,3,4...k-1,建立(i,i+1)的边,流量为+∞,花费为0的边。①

对于i = k,k+1,k+2...n,建立(i,i+1)的边,流量为k-me-ms,花费为0的边。②

对于i = 1,2,3,4...n,建立(i,min(i+k,T))的边,流量为1,花费为si的边。③

建立(S,1)流量为k-me,花费为0的边。④

明显在前两步建边中流过这些边的流量也都是无效流,也就是没有意义。没有意义的边的流量最大是k-me-ms,所以有效流肯定最少是k-me - (k-me-ms) = ms。这也就保证了流量上下界。

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 5e3 + 10;
const int mod = 998244353;
int n,K,ms,me,tot;
int s[mx],c[mx],head[mx];
int pre[mx];
bool vis[mx];
struct node{
	int v,f,c;
	int nxt;
}e[mx];
ll dis[mx]; 
void add(int u,int v,int f,int c){
	e[tot] = {v,f,c,head[u]};
	head[u] = tot++;
	e[tot] = {u,0,-c,head[v]};
	head[v] = tot++;
}
bool spfa(int S,int T)
{
	for(int i=0;i<=T;i++){
		dis[i] = -1e16;
		pre[i] = -1;
	} 
	queue <int> q;
	q.push(S);
	dis[S] = 0;
	while(!q.empty()){
		int now = q.front();
		q.pop();
		vis[now] = 0;
		for(int i=head[now];~i;i=e[i].nxt)
		{
			int v = e[i].v;
			if(e[i].f&&dis[v]<dis[now]+e[i].c){
				dis[v] = dis[now] + e[i].c;
				pre[v] = i;
				if(!vis[v]){
					vis[v] = 1;
					q.push(v);
				}
			}
		}
	}
	return dis[T] != -1e16;
}
ll maxflow(int S,int T){
	ll ans = 0;
	while(spfa(S,T)){
		int mins = inf;
		for(int i=pre[T];~i;i=pre[e[i^1].v]) mins = min(mins,e[i].f);
		for(int i=pre[T];~i;i=pre[e[i^1].v])
		{
			e[i].f -= mins;
			e[i^1].f += mins;	
		}
		ans += dis[T];
	}
	return ans;
}
int main(){
	freopen("delight.in","r",stdin);
	freopen("delight.out","w",stdout);
	scanf("%d%d%d%d",&n,&K,&ms,&me);
	ll ans = 0;
	memset(head,-1,sizeof(head));
	int R = K - me;
	for(int i=1;i<=n;i++) scanf("%d",s+i);
	for(int i=1;i<=n;i++) scanf("%d",c+i),ans += c[i];
	int S = 0,T = n + 1;
	for(int i=1;i<=n;i++){
		if(i+K>n) add(i,T,1,s[i]-c[i]);
		else add(i,i+K,1,s[i]-c[i]);
	}
	for(int i=1;i<=n;i++){
		if(i<K) add(i,i+1,inf,0);
		else add(i,i+1,R-ms,0);
	}
	add(S,1,R,0);
	printf("%lld\n",ans+maxflow(S,T));
	for(int i=0;i<2*n;i+=2){
		if(e[i].f) putchar('E');
		else putchar('S');
	}
	puts("");
	return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值