牛客国庆集训派对Day6 A-Birthday (最小费用最大流详细题解)

牛客国庆集训派对Day6 A-Birthday (最小费用最大流)

题目大意:

宇扬在蛋糕上插了 n (1<=n<=50)支蜡烛,并把蛋糕分为 m (2<=m<=50)个区域。

因为某种原因,他必须把第i根蜡烛插在第ai个区域第bi个区域。区域之间是不相交的。

宇扬在一个区域内同时摆放x支蜡烛就要花费x^{2}的时间。

布置蛋糕所用的总时间是他在每个区域花的时间的和,求布置蛋糕最少时间。

解题思路:
这是一个最小费用最大流的问题,难点在于如何建图,建完图跑一下模板问题就解决了。
下面说一下思路:
首先建立一个源点( 0 ),一个汇点( n+m+1 ),
然后源点与每个蜡烛 i 建立一条 (1,0)流量为1费用为0的边,
然后蜡烛 i 与两个区域ai,bi 分别建立一条 (1,0)流量为1费用为0的边,
最后每个区域与汇点建立对应的流量费用边即可。

难点就在于最后的的流量费用边如何建立,因为耗费的时间和区域内蜡烛的数量成正比,x支蜡烛对应x^{2}的时间,很显然只有1支蜡烛的话,建一条(1,1)流量为1费用为1的边即可,而区域内有多根蜡烛要怎么办呢。

解决这个问题就用到了等差数列的求和公式
在这里插入图片描述
推导一下可得 k^{2}= 1+3+5+……+(2*k-1); 即建立n条流量为1费用为(x * x - (x-1) * (x-1))的边即可。

下面上图(手太残了,图画的有点丑 ):
在这里插入图片描述

Code:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
using namespace std;
const int MAXN = 1111, MAXM = 11111, inf = 1e9;
struct Edge{
	int to, next, cap, flow, cost;
}e[MAXM];
int head[MAXN] ,tol,N;
int pre[MAXN], dis[MAXN];
bool vis[MAXN];

void init(int n){
	N = n;
	tol = 0;
	memset(head,-1,sizeof head);
}

void addedge(int u,int v,int cap,int cost){
	e[tol].to = v;
	e[tol].cap = cap;
	e[tol].cost = cost;
	e[tol].flow = 0;
	e[tol].next = head[u];
	head[u] = tol++;
	e[tol].to = u;
	e[tol].cap = 0;
	e[tol].cost = -cost;
	e[tol].flow = 0;
	e[tol].next = head[v];
	head[v] = tol++;
}

bool spfa(int s,int t){
	queue<int> q;
	memset(pre,-1,sizeof pre);
	memset(vis,0,sizeof vis);
	for(int i = s; i <= t; ++i){
		dis[i] = inf;
	}
	dis[s] = 0;
	vis[s] = 1;
	q.push(s);
	while(!q.empty()){
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i != -1; i = e[i].next) {
			int v = e[i].to;
			if(e[i].cap > e[i].flow && dis[v] > dis[u] + e[i].cost){
				dis[v] = dis[u] + e[i].cost;
				pre[v] = i;
				if(!vis[v]){
					vis[v] = 1;
					q.push(v);
				}
			}
		}
	}
	if(pre[t] == -1) return 0;
	else return 1;
}

int minCostMaxFlow(int s,int t,int &cost){
	int flow = 0;
	cost = 0;
	while(spfa(s,t)){
		int Min = inf;
		for(int i = pre[t]; i != -1; i = pre[e[i^1].to]){
			if(Min > e[i].cap - e[i].flow)
				Min = e[i].cap - e[i].flow;
		}
		for(int i = pre[t]; i != -1; i = pre[e[i^1].to]){
			e[i].flow += Min;
			e[i ^ 1].flow -= Min;
			cost += e[i].cost * Min;
		}
		flow += Min;
	}
	return flow;
}

int main()
{
//#ifndef ONLINE_JUDGE
//	freopen("input.in","r",stdin);
//#endif
	int n,m,x,y,cost;
	cin>>n>>m;
	init(n+m+1);
	for(int i = 1; i <= n; ++i){
		cin>>x>>y;
		addedge(0,i,1,0);
		addedge(i,x+n,1,0);
		addedge(i,y+n,1,0);
	}
	for(int i = 1; i <= m; ++i){
		for(int j=1;j<=50;j++)
			addedge(i+n,m+n+1,1,j*j-(j-1)*(j-1)); //x的平方-(x-1)的平方 
		}
	}
	minCostMaxFlow(0,n+m+1,cost);
	cout<<cost<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43872264/article/details/107718876