牛客國慶集訓派對Day6 A-Birthday (最小費用流)
阿新 • • 發佈:2018-12-13
題目描述
恬恬的生日臨近了。宇揚給她準備了一個蛋糕。 正如往常一樣,宇揚在蛋糕上插了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;
}