牛客国庆集训派对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;
}