题目链接:
https://odzkskevi.qnssl.com/3699857ff0a17d77d1099699cdf4da13?v=1490503337
Description
一块n*m的草坪,有两种高度的草,#表示较高的草,.表示较矮的草,现在要从左往右和从上往下用收割机收割,在相同高度的草坪上收割机不耗油,当高度变化时需要耗花费为a的油,还可以花费b改变任一块草坪的高度,问这n+m排收割机扫过这块草坪需要的最小花费
Input
第一行四整数n,m,a,b分别表示草坪行列数和两种花费,之后一个n*m矩阵表示该草坪(1<=n,m<=50,1<=a,b<=1e5)
Output
输出最小花费
Sample Input
Sample Output
11000
分析:
这道网络流的题目比较抽象,在没有经验的情况下,根本没看出这是一道网络流。
具体做法就是用所有高地与超级源点建立容量为b的边,所有低地和超级汇点建立容量为b的边,相邻的点之间(不管是不是同为高地还是低地)建立容量为a的边。
先来看一种情况
如果b的值大于a,那么从高地往低地的最终流量就为a,如果a>b,那么最终流量就是b。很明显,网络流就相当于给了一种选择,如果高度变化时所需的花费少那么就选择a,如果更改高度花费少就选择b。还有一个问题就是,如果同为高地或者低地,那为什么之间还要建容量为a的边,其实只要当一条流经过某个点时,它已经是决策过了,因为流量的值为正。可以拿全部为低地或者全部为高地思考一下。
同时也给出来自大佬的分析:
把高草坪看做一个集合S,矮草坪看做一个集合E,问题可以的当做是把这n*m个点分成这两个集合所需的最小代价,高草坪变成矮草坪和矮草坪变成高草坪花费代价b,每块草坪和其四周的草坪之间花费代价a表示高度变化的代价(同样高度也要连,因为不知道是否要改变其中某块的高度,而且如果两块草坪最终高度相同那么就会被分在同一个集合中,那么这个代价就不会被统计),即为求这个图的最小割
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<vector> #include<queue> #include<map> #include<set> using namespace std; #define PI acos(-1.0) #define eps 1e-8 #define pb push_back #define MII map<int,int>::iterator #define MLL map<LL,LL>::iterator #define pii pair<int,int> #define SI set<int>::iterator #define SL set<LL>::iterator #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define ls o<<1 #define rs o<<1|1 #define bug printf("bug-------bug-------bug\n") using namespace std; typedef long long ll; typedef unsigned long long ul; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; } const int maxn = 3000;//最大顶点数 const int maxe = 25005;//最大边数 const int inf = 0x3f3f3f3f; int n, m, a, b, src, dst;//src超级源点,dst超级汇点 int cnt;//邻接表存图时总的边的数目 int head[maxn];//邻接表初始化 int d[maxn], cur[maxn];//d为分层图的层次数,cur为当前弧 bool vis[maxn];//用于建立分层图时标记是否访问过 char mp[55][55]; void init() { memset(head, -1, sizeof(head)); cnt = 0; } struct Edge { int from, to, cap, flow, nxt; Edge(){} Edge(int _from, int _to, int _cap, int _flow, int _nxt):from(_from), to(_to), cap(_cap), flow(_flow), nxt(_nxt){} }edge[maxe]; void AddEdge(int u, int v, int w) { edge[cnt] = Edge(u, v, w, 0, head[u]); head[u] = cnt++; edge[cnt] = Edge(v, u, 0, 0, head[v]); head[v] = cnt++; } bool BFS()//建立分层图 { queue<int> q; memset(vis, false, sizeof(vis)); vis[src] = true; d[src] = 0; q.push(src); while(!q.empty()) { int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].nxt) { int v = edge[i].to, c = edge[i].cap; if(!vis[v] && c > 0) { vis[v] = true; d[v] = d[u] + 1; q.push(v); } } } return vis[dst];//是否还有残余网络能到达汇点 } int Stack[maxn], top;//栈优化 ll Dinic() { ll ret = 0; while(BFS()) { //for(int i = 0; i <= dst; i++) //cur[i] = head[i]; memcpy(cur, head, sizeof(head));//当前弧 int u = src, top = 0;//top表示栈顶的弧 while(true) { if(u == dst) { int flow = inf, loc; for(int i = 0; i < top; i++) if(flow > edge[Stack[i]].cap) { flow = edge[Stack[i]].cap; loc = i;//loc表示增广路中容量最小的边编号 } for(int i = 0; i < top; i++)//可行边减去流量 { edge[Stack[i]].cap -= flow; edge[Stack[i]^1].cap += flow; } ret += flow; top = loc;//采用容量最小的边最为栈顶,进行增广 u = edge[Stack[top]].from; } for(int i = cur[u]; i != -1; cur[u] = i = edge[i].nxt)//寻找残余网络中的一条边 if(edge[i].cap && (d[u]+1 == d[edge[i].to])) break; if(cur[u] != -1)//如果找到一条可行边,把这条边放到栈顶,用它尾部的点当做起始点去寻找残余网络中接下去的一条边 { Stack[top++] = cur[u]; u = edge[cur[u]].to; } else//如果没有找到一条可行的边 { if(top == 0)//如果栈中已经没有了可行的边,则表示此次增广完成 break; d[u] = -1;//该点的出边已经无法增广 u = edge[Stack[--top]].from;//采用当前点的的上一个点作为起点去找增广路 } } } return ret; } int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1}; int main() { while(scanf("%d%d%d%d", &n, &m, &a, &b) != EOF) { for(int i = 1; i <= n; i++) scanf("%s", mp[i]+1); src = 0;dst = n*m+1;//源点和汇点 init();//邻接表初始化 for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) { if(mp[i][j] == '#') AddEdge(src, (i-1)*m+j, b); else AddEdge((i-1)*m+j, dst, b); for(int k = 0; k < 4; k++) { int nx = i + dir[k][0]; int ny = j + dir[k][1]; if(nx < 1 || nx > n || ny < 1 || ny > m) continue; AddEdge((i-1)*m+j, (nx-1)*m+ny, a); } } printf("%I64d\n", Dinic()); } return 0; }