1. 程式人生 > >LUOGU 題解 P2045 【方格取數加強版】

LUOGU 題解 P2045 【方格取數加強版】

achieve 判斷 something queue return 但是 swe 設置 import

一道挺好的費用流模板。

當看到只能走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 【方格取數加強版】