LUOGU 題解 P2045 【方格取數加強版】
一道挺好的費用流模板。
當看到只能走K次時,根據直覺盲目猜測,初步判斷可以轉換為網絡流的邊流量限制,即限制匯入匯點的總流量。
然而這題還有一個“方格中的數字”,要求求最大,於是,就有了費用流的費用限制。
但是,費用流的全稱為“最小費用最大流”,但是這題要求求最大“費用”(即數字之和),所以這題其實是“最大費用最大流”???
思考到這裏,來想想怎麽建圖。
First
首先,方格中的數字,我們只能夠取一次,於是我們可以考慮對每個點$x$進行拆點,拆分為“入點”與“出點”,分別記為$x‘$和$x‘‘$,然後連一條邊,即$x‘\xrightarrow{flow=1,cost=a_{i,j}} x‘‘$,其中$a_{i,j}$表示每個點的數字,限制經過每個點獲取的數字。
但是,每個點被取走數字後,並不是不能再走了,而只是數字變為$0$,於是我們再在$x’$與$x‘‘$之間建一條邊$x‘\xrightarrow{flow=inf,cost=0}x‘‘$。
Second
然後呢,每個點可以走到下方的點或右方的點,於是我們將其連接:
設$x_{i,j}$、$x_{i,j+1}$與$x_{i+1,j}$
$x_{i,j}‘‘\xrightarrow{flow=inf,cost=0}x_{i,j+1}‘$。
$x_{i,j}‘‘\xrightarrow{flow=inf,cost=0}x_{i+1,j}‘$。
即表示可以走無數次,但不增加其$cost$。
Third
此時我們還沒有連接源點與匯點,雖然連接哪裏是顯而易見的了……
我們設源點為$S$,匯點為$T$,因為我們只能走$K$次,所以我們把源點流出的流量與流入匯點的流量都設置為$K$,$cost$設為$0$,於是,建邊部分就完成了!
About Network Flow
前面說了,這題的實質是“最大費用最大流”,所以直接照著板子打費用流是不行的。這裏提供兩種解決方法:
1、賦值費用時賦值為相反數,然後原來最大的就變成最小的……然後取$minflow$的相反數輸出。
2、在SPFA部分的判斷條件部分的$cost[y]>cost[x]+edge[i].cost$改為$cost[y]<cost[x]+edge[i].cost$,然後$mincost$就變成了$maxcost$辣。
先貼我的EK代碼。
CODE:
//Luogu P2045
//Solution: EK Network Flow, Minimum Cost Maximum Flow
//Written by MaxDYF
#include <bits/stdc++.h>
using namespace std;
const int NODE = 1000000;
const int EDGE = 1000000;
const int inf = 1e9;
//Index of Edge
struct Edge{
int to, nxt, flow, cost;
}edge[EDGE << 1];
int head[NODE], cnt = 1;
//Indexs of EK Network Flow
int flow[NODE];
int cost[NODE];
int pre[NODE]; //record the last NODE of the present NODE in the road.
int last[NODE];//record the last EDGE of the present NODE.
//The Start and End
int S, T;
//The Indexs to record result
int maxflow, mincost;
//Something for SPFA
bool vis[NODE];
queue<int> que;
//The functions
void add(int, int, int, int);
bool spfa();
void work();
//All the things above here are about the Network Flow.
//And the things under here are about the Problem.
int a[60][60];//record the map
//They are for Spliting NODEs
int start[60][60];
int end[60][60];
//main function
int main()
{
int n, k, cnt = 1;
cin >> n >> k;
//set the Start and the End
S = 0;
T = 2 * n * n + 1;
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= n; ++j)
{
cin >> a[i][j];
a[i][j] = -a[i][j];
//One of the MOST IMPORTANT things: split each NODE into 2 NODEs
start[i][j] = ++cnt;
end[i][j] = ++cnt;
}
}
//Tips:
//In this problem, the "cost" is refer to the Price of the Node.
//So we have to get the "Max 'Cost'" instead of "Min 'Cost'".
//To achieve our goal, we can turn the "cost"
//to its opposite number.
//And get the opposite number of the answer.
//:)
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= n; ++j)
{
//Add edge for these two NODEs
add(start[i][j], end[i][j], 1, a[i][j]);
//set the Flow of the Edge between them to 1
//to make sure that the Number can only be taken once.
add(start[i][j], end[i][j], inf, 0);
//to make sure that this Node can be walked many times.
if(i < n)
add(end[i][j], start[i+1][j], inf, 0);
if(j < n)
add(end[i][j], start[i][j+1], inf, 0);
//just means they are connected,
//and can't get any "cost"(or price) any more.
}
}
add(S, start[1][1], k, 0);
add(end[n][n], T, k, 0);
//make sure there are only "k" roads being collected.
work();
cout << -mincost;
}
//functions
bool spfa()
{
//init
memset(vis, 0, sizeof vis);
memset(flow, 0x3f, sizeof flow);
memset(cost, 0x3f, sizeof cost);
cost[S] = 0;
pre[T] = -1;
que.push(S);
vis[S] = 1;
//main work of SPFA
while (!que.empty())
{
int x = que.front();
que.pop();
vis[x] = 0;
for (int i = head[x]; i; i = edge[i].nxt)
{
int y = edge[i].to;
//if the present node is not the CHEAPEST
if((cost[y] > cost[x] + edge[i].cost) && (edge[i].flow > 0))
{
//update the node
//and its "pre" and "last"
cost[y] = cost[x] + edge[i].cost;
flow[y] = min(flow[x], edge[i].flow);
pre[y] = x;
last[y] = i;
if(!vis[y])
{
vis[y] = 1;
que.push(y);
}
}
}
}
return pre[T] != -1;
}
void add(int x, int y, int flow, int cost)
{
edge[++cnt] = Edge{y, head[x], flow, cost};
head[x] = cnt;
edge[++cnt] = Edge{x, head[y], 0, -cost};
head[y] = cnt;
}
void work()
{
//EK Network Flow Algorithm
//Nothing worth saying.
//Isn't it?
//If you can't understand it,
//you'd better learn The EK Algorithm first.
while(spfa())
{
maxflow += flow[T];
mincost += flow[T] * cost[T];
int now = T;
while(now != S)
{
edge[last[now]].flow -= flow[T];
edge[last[now] ^ 1].flow += flow[T];
now = pre[now];
}
}
}
LUOGU 題解 P2045 【方格取數加強版】