P3980 [NOI2008] 志愿者招募(费用流)

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3980 [NOI2008] 志愿者招募(费用流)相关的知识,希望对你有一定的参考价值。

P3980 [NOI2008] 志愿者招募(费用流)

考虑如何表示 一个人对一段区间的影响。

我们可以这样建边: ( s , t + 1 , i n f , w ) (s,t+1,inf,w) (s,t+1,inf,w)

表示一个人可以对 [ s , t ] [s,t] [s,t]这段区间贡献一点流量,费用为 w w w,并且可以招募 i n f inf inf个人。

同时对于第 i i i天向第 i + 1 i+1 i+1天连容量为 i n f − a i inf-a_i infai,费用为 0 0 0的边。

最后连 ( s t , 1 , i n f , 0 ) , ( n + 1 , e d , i n f , 0 ) (st,1,inf,0),(n+1,ed,inf,0) (st,1,inf,0),(n+1,ed,inf,0)

这样根据最大流最小费用,会优先选择 0 0 0费边,当某一天人不够的时候,就可以用带权边补充流量。

满足 M C M F MCMF MCMF定义。

// Problem: P3980 [NOI2008] 志愿者招募
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3980
// Memory Limit: 125 MB
// Time Limit: 2000 ms
// Date: 2021-12-09 15:26:32
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=1e3+5,M=2e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const int hashmod[4] = 402653189,805306457,1610612741,998244353;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define x first
#define y second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define ios ios::sync_with_stdio(false),cin.tie(nullptr) 
void Print(int *a,int n)
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\\n",a[n]); 

template <typename T>		//x=max(x,y)  x=min(x,y)
void cmx(T &x,T y)
	if(x<y) x=y;

template <typename T>
void cmn(T &x,T y)
	if(x>y) x=y;

int cnt=1,h[N],flow[N],dis[N],vis[N],n,m,st,ed;
ll mc,mf;
int id(int x,int y)
	return (x-1)*n+y;

queue<int>q;
struct edge
	int to,nt,f,w;//f:flow ,w:cost 
e[M];
struct Pre
	int i,u;
pre[N];//记录前驱结点和边的信息,便于更新残余网络,建立反边.(反悔和贪心的思想)	
void add(int u,int v,int f,int w)
	e[++cnt]=v,h[u],f,w,h[u]=cnt;
	e[++cnt]=u,h[v],0,-w,h[v]=cnt;
 
bool spfa()// 跑spfa 
	mst(dis,0x3f),mst(flow,0x3f),mst(vis,0);	//初始化. 
	q.push(st),vis[st]=1,dis[st]=0,pre[ed].u=-1;//预处理 
	while(!q.empty())
		int u=q.front();q.pop();vis[u]=0;
		for(int i=h[u];i;i=e[i].nt)
			int v=e[i].to,f=e[i].f,w=e[i].w;
			if(f>0&&dis[v]>dis[u]+w)
				dis[v]=dis[u]+w;
				pre[v].u=u,pre[v].i=i;
				flow[v]=min(flow[u],f);
				if(!vis[v]) vis[v]=1,q.push(v);
			
		
	
	return pre[ed].u!=-1;

void MCMF()	//MIncost Maxflow 
	while(spfa())
		int u=ed,x=flow[ed];
		mf+=x;
		mc+=x*dis[u];
		while(u!=st)
			e[pre[u].i].f-=x;
			e[pre[u].i^1].f+=x;
			u=pre[u].u;
		
	

int main()
	scanf("%d%d",&n,&m);
	st=0, ed = n+2;
	add(st,1,inf,0),add(n+1,ed,inf,0);
	rep(i,1,n)
		int x;scanf("%d",&x);
		add(i,i+1,inf-x,0);
	
	rep(i,1,m)
		int u,v,w;scanf("%d%d%d",&u,&v,&w);
		add(u,v+1,inf,w);
	
	MCMF();
	printf("%lld\\n",mc);
	return 0;


以上是关于P3980 [NOI2008] 志愿者招募(费用流)的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P3980:[NOI2008]志愿者招募

费用流NOI2008志愿者招募

费用流BZOJ1061[NOI2008]-志愿者招募

1061. [NOI2008]志愿者招募费用流

[BZOJ1061] [Noi2008] 志愿者招募 (费用流)

bzoj1061[NOI2008]志愿者招募 线性规划与费用流