CCF計算機職業資格認證考試題解系列文章為meelo原創,請務必以鏈接形式注明本文地址
CCF CSP 201703-5 引水入城
問題描述
MF城建立在一片高原上。由於城市唯一的水源是位於河谷地帶的湖中,人們在坡地上修築了一片網格狀的抽水水管,以將湖水抽入城市。如下圖所示:
這片管網由 n 行 m 列節點(紅色,圖中 n = 5, m = 6),橫向管道(紫色)和縱向管道(橙色)構成。
行和列分別用 1 到 n 的整數和 1 到 m 的整數表示。第 1 行的任何一個節點均可以抽取湖水,湖水到達第 n 行的任何一個節點即算作引入了城市。
除第一行和最后一行外,橫向相鄰或縱向相鄰的兩個節點之間一定有一段管道,每一段管道都有各自的最大的抽水速率,並需要根據情況選擇抽水還是放水。對於縱向的管道(橙色),允許從上方向下方抽水或從下方向上方放水;如果從圖中的上方向下方抽水,那么單位時間內能通過的水量不能超過管道的最大速率;如果從下方向上方放水,因為下方海拔較高,因此可以允許有任意大的水量。對於橫向的管道(紫色),允許從左向右或從右向左抽水,不允許放水,兩種情況下單位時間流過的水量都不能超過管道的最大速率。
現在MF城市的水務負責人想知道,在已知每個管道單位時間容量的情況下,MF城每單位時間最多可以引入多少的湖水。

這片管網由 n 行 m 列節點(紅色,圖中 n = 5, m = 6),橫向管道(紫色)和縱向管道(橙色)構成。
行和列分別用 1 到 n 的整數和 1 到 m 的整數表示。第 1 行的任何一個節點均可以抽取湖水,湖水到達第 n 行的任何一個節點即算作引入了城市。
除第一行和最后一行外,橫向相鄰或縱向相鄰的兩個節點之間一定有一段管道,每一段管道都有各自的最大的抽水速率,並需要根據情況選擇抽水還是放水。對於縱向的管道(橙色),允許從上方向下方抽水或從下方向上方放水;如果從圖中的上方向下方抽水,那么單位時間內能通過的水量不能超過管道的最大速率;如果從下方向上方放水,因為下方海拔較高,因此可以允許有任意大的水量。對於橫向的管道(紫色),允許從左向右或從右向左抽水,不允許放水,兩種情況下單位時間流過的水量都不能超過管道的最大速率。
現在MF城市的水務負責人想知道,在已知每個管道單位時間容量的情況下,MF城每單位時間最多可以引入多少的湖水。
輸入格式
由於輸入規模較大,我們采用偽隨機生成的方式生成數據。
每組數據僅一行包含 6 個非負整數 n, m, A, B, Q, X 0。其中, n 和 m 如前文所述,表示管網的大小,保證 2 ≤ n, m ≤ 5000;保證 1 ≤ A, B, Q, X 0 ≤ 10 9。
A, B, Q, X 0 是數據生成的參數,我們用如下的方式定義一個數列 { Xi }:
Xi +1 = ( AXi + B) mod Q, ( i ≥ 0)
我們將數列的第 1 項到第 ( n-1) m 項作為縱向管道的單位時間容量,其中 X (s-1)m+t 表示第 s 行第 t 列的節點到第 s+1 行第 t 列管道單位時間的容量;將數列的第 ( n-1) m+1 項到第 ( n-1) m+ (n-2)(m-1) 項(即接下來的 ( n-2)( m-1) 項)作為橫向管道的單位時間容量,其中 X (n-1)m+(s-2)(m-1)+t 表示第 s 行第 t 列的節點到第 s 行第 t+1 列管道單位時間的容量。
每組數據僅一行包含 6 個非負整數 n, m, A, B, Q, X 0。其中, n 和 m 如前文所述,表示管網的大小,保證 2 ≤ n, m ≤ 5000;保證 1 ≤ A, B, Q, X 0 ≤ 10 9。
A, B, Q, X 0 是數據生成的參數,我們用如下的方式定義一個數列 { Xi }:
Xi +1 = ( AXi + B) mod Q, ( i ≥ 0)
我們將數列的第 1 項到第 ( n-1) m 項作為縱向管道的單位時間容量,其中 X (s-1)m+t 表示第 s 行第 t 列的節點到第 s+1 行第 t 列管道單位時間的容量;將數列的第 ( n-1) m+1 項到第 ( n-1) m+ (n-2)(m-1) 項(即接下來的 ( n-2)( m-1) 項)作為橫向管道的單位時間容量,其中 X (n-1)m+(s-2)(m-1)+t 表示第 s 行第 t 列的節點到第 s 行第 t+1 列管道單位時間的容量。
輸出格式
輸出一行一個整數,表示MF城每單位時間可以引入的水量。
注意計算過程中有些參數可能超過32位整型表示的最大值,請注意使用64位整型存儲相應數據。
注意計算過程中有些參數可能超過32位整型表示的最大值,請注意使用64位整型存儲相應數據。
樣例輸入
3 3 10 3 19 7
樣例輸出
38
樣例說明
使用參數得到數列 {
Xi }={ 7, 16, 11, 18, 12, 9, 17, 2, 4, … },按照輸入格式可以得到每個管道的最大抽水量如下圖所示:
在標准答案中,單位時間可以引水 38 單位。所有縱向管道均向下抽水即可,不需要橫向管道抽水,也不需要向上放水。

在標准答案中,單位時間可以引水 38 單位。所有縱向管道均向下抽水即可,不需要橫向管道抽水,也不需要向上放水。
樣例輸入
2 5 595829232 749238243 603779819 532737791
樣例輸出
1029036148
樣例輸入
5 2 634932890 335818535 550589587 977780683
樣例輸出
192923706
樣例輸入
5 5 695192542 779962396 647834146 157661239
樣例輸出
1449991168
評測用例規模與約定
共有10組評測數據,每組數據的參數規模如下所示:
測試點編號 | n | m |
1 | =2 | =1000 |
2 | =1000 | =2 |
3 | =1000 | =2 |
4 | =5 | =5 |
5 | =10 | =10 |
6 | =100 | =100 |
7 | =500 | =500 |
8 | =1000 | =1000 |
9 | =2000 | =2000 |
10 | =5000 | =5000 |
解析
這是一個最大流的問題,湖是源,城市是匯。
下面實現了ford-fulkerson算法,只能通過50%的數據。
有更好的方法求告知!
代碼
C++
#include <iostream> #include <vector> #include <queue> #include <utility> #include <climits> using namespace std; long long A, B, Q, X; int numVertex; int nextRandom() { X = (A * X + B) % Q; return X; } struct Edge { int v; // vertex int w; // weight Edge(int v_, int w_) : v(v_), w(w_) {} }; bool bfs(vector<vector<Edge> > &rgraph, int s, int t, vector<pair<int,int> > &parents) { queue<int> q; vector<bool> visited(numVertex); q.push(s); while(!q.empty()) { int u = q.front(); q.pop(); for(int i=0; i<rgraph[u].size(); i++) { int v = rgraph[u][i].v; if(!visited[v] && rgraph[u][i].w>0) { visited[v] = true; parents[v] = make_pair(u,i); q.push(v); if(v == t) return true; } } } return false; } long long fordFulkerson(vector<vector<Edge> > &rgraph, int s, int t) { long long maxFlow = 0; vector<pair<int,int> > parents(numVertex); while(bfs(rgraph, 0, 1, parents)) { int pathFlow = INT_MAX; for(int v=t; v!=s; ) { int u=parents[v].first; int ui = parents[v].second; pathFlow = min(pathFlow, rgraph[u][ui].w); v = u; } maxFlow += pathFlow; // cout << pathFlow << " " << maxFlow << endl; for(int v=t; v!=s; ) { int u = parents[v].first; int ui = parents[v].second; if(rgraph[u][ui].w!=INT_MAX) rgraph[u][ui].w -= pathFlow; int vi = -1; for(int i=0; i<rgraph[v].size(); i++) { if(rgraph[v][i].v == u) { vi = i; } } if(vi!=-1 && rgraph[v][vi].w!=INT_MAX) rgraph[v][vi].w += pathFlow; v = u; } } return maxFlow; } int main() { int N, M; cin >> N >> M >> A >> B >> Q >> X; numVertex = N * M + 2; // 0:source, 1:sink, vector<vector<Edge> > graph(numVertex, vector<Edge>()); int offset = 2; // construct graph for(int n=0; n<N-1; n++) { for(int m=0; m<M; m++) { int from = n*M+m+offset; int to = from+M; nextRandom(); graph[from].push_back(Edge(to, X)); graph[to].push_back(Edge(from, INT_MAX)); } } for(int m=0; m<M; m++) { int from = 0; int to = m+offset; graph[from].push_back(Edge(to, INT_MAX)); } for(int m=0; m<M; m++) { int from = (N-1)*M+m+offset; int to = 1; graph[from].push_back(Edge(to, INT_MAX)); } long long maxFlow = 0; for(int n=1; n<N-1; n++) { for(int m=0; m<M-1; m++) { int from = n*M+m+offset; int to = from+1; nextRandom(); graph[from].push_back(Edge(to, X)); graph[to].push_back(Edge(from, X)); } } maxFlow += fordFulkerson(graph, 0, 1); cout << maxFlow; }