1. 程式人生 > 實用技巧 >[ICPC-Beijing 2006]狼抓兔子(網路最大流+最短路)

[ICPC-Beijing 2006]狼抓兔子(網路最大流+最短路)

題目描述

現在小朋友們最喜歡的"喜羊羊與灰太狼",話說灰太狼抓羊不到,但抓兔子還是比較在行的,而且現在的兔子還比較笨,它們只有兩個窩,現在你做為狼王,面對下面這樣一個網格的地形:

左上角點為 (1,1), 右下角點為 (N,M) (上圖中 N=3, M=4).有以下三種類型的道路:

\((x,y)⇌(x+1,y)(x,y)\rightleftharpoons(x+1,y)(x,y)⇌(x+1,y)\)

\((x,y)⇌(x,y+1)(x,y)\rightleftharpoons(x,y+1)(x,y)⇌(x,y+1)\)

\((x,y)⇌(x+1,y+1)(x,y)\rightleftharpoons(x+1,y+1)(x,y)⇌(x+1,y+1)\)

道路上的權值表示這條路上最多能夠通過的兔子數,道路是無向的。左上角和右下角為兔子的兩個窩,開始時所有的兔子都聚集在左上角 (1,1) 的窩裡,現在它們要跑到右下角 (N,M) 的窩中去,狼王開始伏擊這些兔子。當然為了保險起見,如果一條道路上最多通過的兔子數為 K,狼王需要安排同樣數量的 K 只狼,才能完全封鎖這條道路,你需要幫助狼王安排一個伏擊方案,使得在將兔子一網打盡的前提下,參與的狼的數量要最小。因為狼還要去找喜羊羊麻煩。

輸入格式

第一行兩個整數 N,M,表示網格的大小。

接下來分三部分。

第一部分共 N 行,每行 M−1 個數,表示橫向道路的權值。

第二部分共 N-1 行,每行 M 個數,表示縱向道路的權值。

第三部分共 N−1 行,每行 M-1 個數,表示斜向道路的權值。

輸出格式

輸出一個整數,表示參與伏擊的狼的最小數量。

輸入輸出樣例

輸入

3 4
5 6 4
4 3 1
7 5 3
5 6 7 8
8 7 6 5
5 5 5
6 6 6

輸出

14

資料規模與約定

對於全部的測試點,保證 \(3≤N,M≤1000\),所有道路的權值均為不超過 10^6 的正整數。

Solution

顯然
暴力建圖直接跑就,,,
但是資料隨便卡\(TLE\)
但是正確性是沒有任何問題的
注意建邊的時候二維轉換一維的對應關係

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#define min(a, b) ({register int AA = a, BB = b; AA < BB ? AA : BB;})
using namespace std;

inline int read(){
	int x = 0, w = 1;
	char ch = getchar();
	for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * w;
}

const int ss = 3000010;

struct node{
	int to, nxt, w;
}edge[ss << 2];

int head[ss], tot = 1;
inline void add(register int u, register int v, register int w){
	edge[++tot].to = v;
	edge[tot].nxt = head[u];
	edge[tot].w = w;
	head[u] = tot;
}

int dis[ss], cur[ss];
int n, m, s, t;
bool vis[ss];
queue<int> q;
inline bool spfa(register int s){
	for(register int i = 0; i <= t; i++)
		dis[i] = 0x3f3f3f3f, cur[i] = head[i];
	dis[s] = 0;
	q.push(s);
	while(!q.empty()){
		register int u = q.front();
		q.pop();
		vis[u] = 0;
		for(register int i = head[u]; i; i = edge[i].nxt){
			register int v = edge[i].to;
			if(dis[v] > dis[u] + 1 && edge[i].w){
				dis[v] = dis[u] + 1;
				if(!vis[v]) q.push(v), vis[v] = 1;
			}
		}
	}
	return dis[t] != 0x3f3f3f3f;
}

inline int dfs(register int u, register int flow){
	register int res = 0;
	if(u == t) return flow;
	for(register int i = cur[u]; i; i = edge[i].nxt){
		cur[u] = i;
		register int v = edge[i].to;
		if(dis[v] == dis[u] + 1 && edge[i].w){
			if(res = dfs(v, min(flow, edge[i].w))){
				edge[i].w -= res;
				edge[i ^ 1].w += res;
				return res;
			}
		}
	}
	return 0;
}

long long maxflow;
inline long long dinic(){
	register long long minflow = 0;
	while(spfa(s)){
		while(minflow = dfs(s, 0x7fffffff))
			maxflow += minflow;
	}
	return maxflow;
}

inline int change(register int i, register int j){
	return (i - 1) * m + j;
}

signed main(){
	n = read(), m = read();
	s = 1, t = n * m;
	for(register int i = 1; i <= n; i++)
		for(register int j = 1; j <= m - 1; j++){
			//cout << change(i, j) << " " << change(i, j) + 1 << endl;
			register int x = read();
			add(change(i, j), change(i, j) + 1, x);
			add(change(i, j) + 1, change(i, j), 0);
			add(change(i, j) + 1, change(i, j), x);
			add(change(i, j), change(i, j) + 1, 0);
		}
	for(register int i = 1; i <= n - 1; i++)
		for(register int j = 1; j <= m; j++){
			//cout << change(i, j) << " " << change(i, j) + m << endl;
			register int x = read();
			add(change(i, j), change(i, j) + m, x);
			add(change(i, j) + m, change(i, j), 0);
			add(change(i, j) + m, change(i, j), x);
			add(change(i, j), change(i, j) + m, 0);
		}
	for(register int i = 1; i <= n - 1; i++)
		for(register int j = 1; j <= m - 1; j++){
			//cout << change(i, j) << " " << change(i, j) + m + 1 << endl;
			register int x = read();
			add(change(i, j), change(i, j) + m + 1, x);
			add(change(i, j) + m + 1, change(i, j), 0);
			add(change(i, j) + m + 1, change(i, j), x);
			add(change(i, j), change(i, j) + m + 1, 0);
		}
	printf("%lld\n", dinic());
	return 0;
}

正解最短路
從左下角向右上角攔截
\(Dij\)
一樣的,注意建圖的時候座標轉換

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;

inline int read() {
    int x = 0, w = 1;
    char ch = getchar();
    for (; ch > '9' || ch < '0'; ch = getchar())
        if (ch == '-')
            w = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
    return x * w;
}

const int ss = 6003000;

struct e {
    int to, w, nxt;
} edge[ss << 2];

int tot, head[ss];
inline void add(int u, int v, int w) {
    edge[++tot].to = v;
    edge[tot].w = w;
    edge[tot].nxt = head[u];
    head[u] = tot;
}

struct node {
    int pos, dis;
    node(int a, int b) {
        pos = a;
        dis = b;
    }
    inline bool operator<(const node &x) const { return dis > x.dis; }
};

priority_queue<node> q;
bool vis[ss];
int dis[ss];

inline void Dij(int s) {
    memset(dis, 0x3f, sizeof dis);
    memset(vis, 0, sizeof vis);
    dis[s] = 0;
    q.push(node(s, 0));
    while (!q.empty()) {
        node tmp = q.top();
        q.pop();
        int u = tmp.pos;
        if (vis[u])
            continue;
        vis[u] = 1;
        for (int i = head[u]; i; i = edge[i].nxt) {
            int v = edge[i].to;
            if (dis[v] > dis[u] + edge[i].w) {
                dis[v] = dis[u] + edge[i].w;
                q.push(node(v, dis[v]));
            }
        }
    }
}

int n, m, s, t;
inline int id(register int x, register int y, register int op){
	return (x - 1) * (m - 1) + y + op * (n - 1) * (m - 1);
}

signed main(){
	n = read(), m = read();
	s = 0 ,t = (n - 1) * (m - 1) * 2 + 1;
	for(register int i = 1; i <= n; ++i){
		for(register int j = 1; j < m; ++j){
			register int x = read();
			if(i == 1) add(id(i, j, 1), t, x), add(t, id(i, j, 1), x);
			else if(i == n) add(id(i - 1, j, 0), s, x), add(s, id(i - 1, j, 0), x);
			else add(id(i, j, 1), id(i - 1, j, 0), x), add(id(i - 1, j, 0), id(i, j, 1), x);
		}
	}
	for(register int i = 1; i < n; ++i){
		for(register int j = 1; j <= m; ++j){
			register int x = read();
			if(j == 1) add(id(i, j, 0), s, x), add(s, id(i, j, 0), x);
			else if(j == m) add(id(i, j - 1, 1), t, x), add(t, id(i, j - 1, 1), x);
			else add(id(i, j, 0), id(i, j - 1, 1), x), add(id(i, j - 1, 1), id(i, j, 0), x);
		}
	}
	for(register int i = 1; i < n; ++i){
		for(register int j = 1; j < m; ++j){
			register int x = read();
			add(id(i, j, 0), id(i, j, 1), x);
			add(id(i, j, 1), id(i, j, 0), x);
		}
	}
	Dij(s);
	printf("%d\n", dis[t]);
	return 0;
}