P1251 餐巾计划问题 网络流之最小费用流

P1251
因为是一天中有不同的事务,现将一天拆点为晚上i 和早上i+N。
流向i的流是脏毛巾,流向早上的为干净毛巾。
建图:
设一源点s,汇点t。

  1. s - > i 流量x,花费0. 表示到晚上有x条脏毛巾产生。
  2. i + N -> t ,流量 x , 花费0 .表示每天早上向汇点提供x条干净毛巾,如果满流,则此天的毛巾够用。
  3. i -> i + 1 . 流量INF , 花费0 。表示第i天晚上的脏毛巾可以不洗,留到i+1天晚上。
  4. i -> i + N + m . 流量INF ,花费fs 。表示第i天晚上的脏毛巾可以送去快洗,到i+m天早上可以用。
  5. i -> i + N + n . 流量INF,花费sl。表示第i天晚上的脏毛巾可以送去慢西,到弟i+n天早上可以用。
  6. s -> i + N ,流量INF,花费p。表示早上可以购买毛巾。
    Code:
#include <bits/stdc++.h>
#define LL long long 
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 5e4 + 666 ;
struct Node{
	int u , v , next1 , flow , cost  ;
	Node( int u = 0 , int v = 0 , int flow = 0 , int cost = 0 , int next1 = 0 ):u(u),v(v),flow(flow),cost(cost),next1(next1){}
}G[AX*10];
int head[AX];
LL dis[AX] ; 
int pre[AX] ;
int idx[AX*10] ; 
int fl[AX] ;
int tot ; 
int s, t ;
bool vis[AX] ;  
void addEdge( int u , int v , int w , int c ){
	G[tot] = Node( u , v , w , c , head[u] ) ; head[u] = tot ++ ;  
	G[tot] = Node( v , u , 0 , -c, head[v] ) ; head[v] = tot ++ ; 
}

int SPFA( ){
	memset( dis , INF , sizeof(dis) ) ; 
	memset( fl , INF , sizeof(fl) ) ; 
	memset( vis , false , sizeof(vis) ) ; 
	queue<int>q ;
	q.push(s) ; 
	dis[s] = 0 ;
	pre[t] = 0 ; 
	while( !q.empty() ) {
		int u = q.front(); q.pop() ; 
		vis[u] = false;
		for( int i = head[u] ; ~i ; i = G[i].next1 ) {
			int v = G[i].v ; 
			if( G[i].flow && dis[u] + G[i].cost < dis[v] ){
				dis[v] = dis[u] + G[i].cost ;  
				pre[v] = u ;
				idx[v] = i ; 
				fl[v] = min( fl[u] , G[i].flow ) ;
				if( !vis[v] ){
					vis[v] = true ; 
					q.push(v) ; 
				}
			}
		}
	}return pre[t] ; 
}
int main(){
	int N ; 
	tot = 0 ;
	memset( head , -1, sizeof(head) ) ;
	scanf("%d",&N) ;
	int x ; 
	s = 0 ; 
	t = 2 * N + 1 ;
	for( int i = 1 ; i <= N ; i++ ){
		scanf("%d",&x);
		addEdge( s , i , x , 0 ) ;
		addEdge( i + N , t , x , 0 ) ; 
	}
	int p , m , fs , n , sl ; 
	scanf("%d%d%d%d%d",&p,&m,&fs,&n,&sl);
	for( int i = 1 ; i <= N ; i++ ){
		if( i + 1 <= N ) addEdge( i , i + 1 , INF , 0 ) ;
		if( i + m <= N ) addEdge( i , i + m + N , INF , fs ) ; 
		if( i + n <= N ) addEdge( i , i + n + N , INF , sl ) ;
		addEdge( s , i + N , INF , p ) ;
	}
	LL rs_cost = 0 ; 
	while( SPFA() ){
		rs_cost += 1LL* fl[t] * dis[t] ; 
		for( int i = t ; i != s ; i = pre[i] ){
			G[idx[i]].flow -= fl[t] ;
			G[idx[i]^1].flow += fl[t] ;
		}
	}
	printf("%lld\n",rs_cost);
	return 0 ; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值