1. 程式人生 > >牛客國慶集訓派對Day6 A-Birthday (最小費用流)

牛客國慶集訓派對Day6 A-Birthday (最小費用流)

題目描述

恬恬的生日臨近了。宇揚給她準備了一個蛋糕。 正如往常一樣,宇揚在蛋糕上插了n支蠟燭,並把蛋糕分為m個區域。因為某種原因,他必須把第i根蠟燭插在第ai個區域或第bi個區域。區域之間是不相交的。宇揚在一個區域內同時擺放x支蠟燭就要花費x2的時間。宇揚佈置蛋糕所用的總時間是他在每個區域花的時間的和。 宇揚想快些見到恬恬,你能告訴他佈置蛋糕最少需要多少時間嗎?

輸入描述:

第一行包含兩個整數n,m(1 ≤ n ≤ 50, 2≤ m≤ 50)。
接下來n行,每行兩個整數ai,bi(1 ≤ ai, bi ≤ m)。

輸出描述:

一個整數表示答案。

示例1

輸入

複製

3 3
1 2
1 2
1 2

輸出

複製

5

示例2

輸入

複製

3 3
1 2
2 3
1 3

輸出

複製

3

解題思路:最直接的思路,暴力搜尋,列舉2^50種情況。加上剪枝可過(記錄最小的答案)。另外一種思路,要使的和最小,肯定是要使數字儘可能地平均,因此用優先佇列維護每一個數字,然後不斷地嘗試把最大的那個數字移到其他數字裡去,看看答案會不會減小,然後更新答案。

最好的思路是費用流。

每個蠟燭建一個點。每個區域建一個點

建立一個超級源點連向每一個蠟燭,流量為1,費用為0.

建立一個超級匯點,每一個區域向匯點連邊,記為邊集E,流量為INF,費用初始為1.

每個蠟燭向他的區域連邊,流量為1,費用為0.

我們比較關心邊集E,這裡的邊流量每+1,他的費用都要更新,因此我們每次SPFA後,都要判斷增廣路徑是否經過這個邊集E,然後更新這條邊的費用,反向邊也要更新。

如果不想更新費用,可以對於每一個區域再新建50個節點,每個節點的費用為平方數之差,這樣子直接跑就OK。

新建50個的程式碼

#include <iostream>
#include <string.h>
#include <math.h>
#include <vector>
#include <algorithm>
#include <stdio.h>
#include <queue>
using namespace std;
const int MAXN = 4010;
const int INF = 0x3f3f3f3f;
typedef long long ll;
 
struct edge
{
    int u, v, cap, cost, next;
} e[4 * MAXN];
 
int edge_num;
int head[MAXN];
 
void insert_edge(int u, int v, int cap, int cost)
{
    e[edge_num].u = u;
    e[edge_num].v = v;
    e[edge_num].cap = cap;
    e[edge_num].cost = cost;
    e[edge_num].next = head[u];
    head[u] = edge_num++;
 
    e[edge_num].u = v;
    e[edge_num].v = u;
    e[edge_num].cap = 0;
    //注意這裡
    e[edge_num].cost = -cost;
    //注意這裡
    e[edge_num].next = head[v];
    head[v] = edge_num++;
 
 
 
}
 
int dis[MAXN];
int pre[MAXN];
bool vis[MAXN];
bool spfa(int s, int t)
{
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    memset(pre, -1, sizeof(pre));
 
    dis[s] = 0;
    vis[s] = 1;
    queue<int> que;
    que.push(s);
    while (!que.empty())
    {
        int tp = que.front();
        que.pop();
        vis[tp] = 0;
        for (int i = head[tp]; ~i; i = e[i].next)
        {
            int v = e[i].v;
            int cost = e[i].cost;
 
            if (e[i].cap && dis[v] > dis[tp] + cost)
            {
                dis[v] = dis[tp] + cost;
                pre[v] = i;
                if (!vis[v])
                {
                    vis[v] = 1;
                    que.push(v);
                }
            }
        }
    }
    if (dis[t] == INF)
        return false;
    return true;
}
 
pair<int, int> MCMF(int s, int t)
{
    int maxflow = 0;
    int mincost = 0;
    int minc;
    while (spfa(s, t))
    {
        minc = INF;
        int cost = 0;
        for (int i = pre[t]; ~i; i = pre[e[i].u])
            minc = min(minc, e[i].cap);
        for (int i = pre[t]; ~i; i = pre[e[i].u])
        {
            e[i].cap -= minc;
            e[i ^ 1].cap += minc;
            cost += minc * e[i].cost; //flow*unit cost=total cost
 
        }
        mincost += cost;
        maxflow += minc;
    }
    return make_pair(mincost, maxflow);
}
 
 
 
int tot = 0;
int in[MAXN];
 
int out[MAXN];
 
int ssout[MAXN][51];
 
 
 
int S, SK, T;
 
 
int main()
{
 
    edge_num = 0;
    memset(head, -1, sizeof(head));
    tot = 0;
 
    int N, M;
    scanf("%d%d", &N, &M);
 
    S = ++tot;
 
    for (int i = 1; i <= N; i++)
        in[i] = ++tot;
 
    for (int i = 1; i <= M; i++)
    {
        out[i] = ++tot;
        for(int j=1;j<=50;j++){
            ssout[i][j]=++tot;
        }
    }
    T = ++tot;
 
    int a,b;
    for(int i=1;i<=N;i++){
        scanf("%d%d",&a,&b);
        insert_edge(in[i],out[b],1,0);
        insert_edge(in[i],out[a],1,0);
    }
 
    for(int i=1;i<=M;i++){
        for(int j=1;j<=50;j++){
            insert_edge(out[i],ssout[i][j],1,j*j-(j-1)*(j-1));
        }
    }
 
 
    for(int i=1;i<=N;i++){
        insert_edge(S,in[i],1,0);
    }
 
    for(int i=1;i<=M;i++){
        for(int j=1;j<=50;j++)
        insert_edge(ssout[i][j],T,1,0);
    }
 
    printf("%d\n", MCMF(S, T).first);
 
 
    return 0;
}

修改邊費用的程式碼

#include <iostream>
#include <string.h>
#include <math.h>
#include <vector>
#include <algorithm>
#include <stdio.h>
#include <queue>
using namespace std;
const int MAXN = 4010;
const int INF = 0x3f3f3f3f;
typedef long long ll;
 
struct edge
{
    int u, v, cap, cost, next;
} e[4 * MAXN];
 
int edge_num;
int head[MAXN];
 
int  insert_edge(int u, int v, int cap, int cost)
{
    e[edge_num].u = u;
    e[edge_num].v = v;
    e[edge_num].cap = cap;
    e[edge_num].cost = cost;
    e[edge_num].next = head[u];
    head[u] = edge_num++;
 
    e[edge_num].u = v;
    e[edge_num].v = u;
    e[edge_num].cap = 0;
    //注意這裡
    e[edge_num].cost = -cost;
    //注意這裡
    e[edge_num].next = head[v];
    head[v] = edge_num++;
 
    return edge_num-2;
 
}
int wm[5006];
bool im[MAXN];
bool om[MAXN];
int dis[MAXN];
int pre[MAXN];
bool vis[MAXN];
bool spfa(int s, int t)
{
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    memset(pre, -1, sizeof(pre));
 
    dis[s] = 0;
    vis[s] = 1;
    queue<int> que;
    que.push(s);
    while (!que.empty())
    {
        int tp = que.front();
        que.pop();
        vis[tp] = 0;
        for (int i = head[tp]; ~i; i = e[i].next)
        {
            int v = e[i].v;
            int cost = e[i].cost;
 
            if (e[i].cap && dis[v] > dis[tp] + cost)
            {
                dis[v] = dis[tp] + cost;
                pre[v] = i;
                if (!vis[v])
                {
                    vis[v] = 1;
                    que.push(v);
                }
            }
        }
    }
    if (dis[t] == INF)
        return false;
    return true;
}
 
pair<int, int> MCMF(int s, int t)
{
    int maxflow = 0;
    int mincost = 0;
    int minc;
    while (spfa(s, t))
    {
        minc = INF;
        int cost = 0;
        for (int i = pre[t]; ~i; i = pre[e[i].u])
            minc = min(minc, e[i].cap);
        for (int i = pre[t]; ~i; i = pre[e[i].u])
        {
            e[i].cap -= minc;
            e[i ^ 1].cap += minc;
            cost += minc * e[i].cost; //flow*unit cost=total cost
 
            if(im[i]){
                mincost-=(wm[e[i].cost]-1)*(wm[e[i].cost]-1);
                e[i].cost=(wm[e[i].cost]+1)*(wm[e[i].cost]+1);
            }
 
            if(om[i]){
                mincost+=(wm[-e[i].cost]-1)*(wm[-e[i].cost]-1);
                e[i].cost=-(wm[-e[i].cost]+1)*(wm[-e[i].cost]+1);
            }
 
        }
        mincost += cost;
        maxflow += minc;
    }
    return make_pair(mincost, maxflow);
}
 
 
 
int tot = 0;
int in[MAXN];
 
int out[MAXN];
 
 
 
 
int S, SK, T;
 
 
int main()
{
    for(int j=1;j<5000;j++)
        for(int i=1;i<=50;i++)
            if(i*i==j)
                wm[j]=i;
    edge_num = 0;
    memset(head, -1, sizeof(head));
    tot = 0;
 
    int N, M;
    scanf("%d%d", &N, &M);
 
    S = ++tot;
    for (int i = 1; i <= N; i++)
        in[i] = ++tot;
 
    for (int i = 1; i <= M; i++)
    {
        out[i] = ++tot;
    }
    T = ++tot;
 
    int a,b;
    for(int i=1;i<=N;i++){
        scanf("%d%d",&a,&b);
        insert_edge(in[i],out[b],1,0);
        insert_edge(in[i],out[a],1,0);
    }
 
    for(int i=1;i<=M;i++){
        int ee=insert_edge(out[i],T,N,1);
        im[ee]=1;
        om[ee+1]=1;
    }
 
 
    for(int i=1;i<=N;i++){
        insert_edge(S,in[i],1,0);
    }
 
 
    printf("%d\n", MCMF(S, T).first);
 
 
    return 0;
}