牛客国庆集训派对Day6 A-Birthday (最小费用最大流)
Birthday
https://ac.nowcoder.com/acm/contest/206/A
牛客国庆集训派对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; }