Landscaping 最大流 / 最小割

Preparations for a good harvest in Spring start now and farmer John is preparing his field for a good season. He went over budget last year, as the tractors moving up and down the hills needed more fuel than he expected.

When harvesting, his tractors need to move both horizontally and vertically across all the land. In the image, you can see a region at low altitude in light green and a region at high altitude in dark green. When harvesting, his tractors will have to cross all the hills marked in red and they will have to go up or down 8 times.

This year, he is wondering whether he should level some parts of his field before sowing in order to lower his harvesting costs later on. Can you help him decide where the bulldozers should work in order to lower his costs? Farmer John knows that his tractors need A additional euros when moving between adjacent patches of land at different heights. He can also pay B euros to either increase or  decrease the height of any patch in his field.

What is the minimum amount of money he will have to pay this season?

Task

Given a description of the field, the price to change the height of a patch of land and the price his tractors pay when moving between adjacent patches, the goal is to find out the minimum amount that farmer John will have to pay this year.

Input

The first line consists of 4 space separated integers,

N,M,A and B.N and M represent  the dimensions of his N×M field, A represents the cost to move between adjacent patches of land at different levels and B is the cost to change any patch of land. Universidade do Porto Computer Science Department

The next N lines each have M characters and represent farmer John’s field. A ’.’ signals a patch of land at a low level and a ’#’ represents a patch of land at a high level.

Constraints 1 ≤ N, M ≤ 50 Size of the field. 1 ≤ A, B ≤ 100 000 Cost to change any height or to move between adjacent patches.

Output

You should output a single line with a single integer representing the minimum amount of money that farmer John will have to pay.

Sample Input

5 4 1000 2000

...#

#..#

...#

##..

###.

Sample Output

11000

题目大意:Farmer John 有一块n*m的田地,他要用推土机推每一行和每一列。田地有高有低,相邻的地高度不同就要付出a的代价,改动一块地的高度需要付出b的代价。问 所要求的代价和的最小值。

解法:因为是将田地分为两类,所以联想到求最小割。

那么如何建图呢?

首先,设立一个源点s和一个汇点t。让所有低地和s连一条代价为b的边,所有高地和t连一条代价为b的边。 此时高地和低地之间是不相连的,最小割,也就是最大流等于零。

然后将所有 位置相邻 且 高低不同 的地之间连一条代价为a的边。

如图,在求最小割的时候,要么把高低地之间的边割掉,要么把地和源点/汇点之间的边割掉。

这样原图的最小割就等同于将其中一些土地的高度改动的代价和相邻高度 不等的土地之间的代价总和的最小值了。

代码:

#include<iostream>
#include<cstdio>
#include<string.h>
#include<vector>
#include<queue>
#include<cmath>
using namespace std;

const  int maxv=1e4+10,  INF=1e9+10;
const int dx[4]={-1, 0, 1, 0},
            dy[4]={0, -1, 0, 1};

struct edge{ int to;  int  cap;  int rev;};
vector<edge> G[maxv];
int level[maxv],iter[maxv];
bool used[maxv];

void add_edge( int from, int to,  int cap)
{
    G[from].push_back((edge){to, cap, G[to].size()});
    G[to].push_back((edge){from, 0, G[from].size()-1});
}

void bfs( int s){
    memset(level, -1, sizeof(level));
    queue<int> que;
    level[s]=0;
    que.push(s);
    while (!que.empty())
    {
         int v=que.front();
        que.pop();
        for ( int i=0; i<G[v].size(); i++)
        {
            edge &e=G[v][i];
            if (e.cap > 0 && level[e.to] < 0)
            {
                level[e.to] = level[v] + 1;
                que.push(e.to);
            }
        }
    }
}

int dfs( int v,  int t,   int  f)
{
    if (v==t) return f;
    for ( int &i=iter[v]; i<G[v].size(); i++)
    {
        edge &e=G[v][i];
        if (e.cap > 0 && level[v] < level[e.to]){
             int  d=dfs(e.to, t, min(f, e.cap));
            if (d>0){
                e.cap -= d;
                G[e.to][e.rev].cap+=d;
                return d;
            }            
        }
    }    
    return 0;
}

 long long  max_flow( int s, int t){
     long long  flow=0;
    for (;;){
        bfs(s);
        if (level[t]<0) return flow;
        memset(iter, 0, sizeof(iter));
         int  f;
        while ((f = dfs(s,t,INF))>0)
        flow += (long long)f;
    }
}

bool f[500][500];
int main()
{
     int n,m,s,t;
     long long  a,b;
    char ss[500];

    cin>>n>>m>>a>>b;   //a是相邻高度不同的代价,b是修改的代价
    
    s=n*m+1;                       //s代表低地集合
    t=s+1;                       //t代表高地集合
    for ( int  i=0; i<n; i++)
    {
        scanf("%s", &ss);
        for ( int j=0; j<m; j++)
        {
            if (ss[j]=='.')            //是一块低地  
            {
                f[i][j]=0;
                add_edge(s, i*m+j, b);    //低地和s连边
            }
            else                   //是一块高地
            {
                f[i][j]=1;
                add_edge(i*m+j, t, b);    //高地和t连边
            }
        }
    }

    for ( int  i=0; i<n; i++)
    {
        for ( int  j=0; j<m; j++)
        {
            for (int k=0; k<4; k++)
            {
                int x=i+dx[k],y=j+dy[k];
                if (x>=0  && x<n && y>=0 && y<m)
                {
                    add_edge(i*m+j,  x*m+y , a);
                }
            }
        }
    }

    cout<<max_flow(s, t);

    return 0;
}

刚开始在建边的时候采取了用当前土地与左边的土地  和 上边的土地比较,然后建双向边的办法。不知道为什么WA了,后来改成将当前土地和上下左右相比较,建单向边的办法,就A了。= =

猜你喜欢

转载自blog.csdn.net/qq_41703679/article/details/81392725