【Luogu P1935】[國家集訓隊]圈地計劃
阿新 • • 發佈:2019-02-05
scan 區域 mod 不可 delta 增加 網絡 變化 商業區 相鄰(相鄰是指兩個格子有公共邊)有 \(K\) 塊(顯然 \(K\) 不超過 \(4\))類型不同於\((I,j)\)的區域,則這塊區域能增加 \(k\times C_{ij}\) 收益。
題目
最近房地產商 GDOI (Group of Dumbbells Or Idiots) 從 NOI (Nuts Old Idiots) 手中得到了一塊開發土地。
據了解,這塊土地是一塊矩形的區域,可以縱橫劃分為 \(N\times M\) 塊小區域。GDOI 要求將這些區域分為商業區和工業區來開發。
根據不同的地形環境,每塊小區域建造商業區和工業區能取得不同的經濟價值。更具體點,對於第 \(i\) 行第 \(j\) 列的區域,建造商業區將得到 \(A_{ij}\) 收益,建造工業區將得到 \(B_{ij}\) 收益。
另外,不同的區域連在一起可以得到額外的收益,即如果區域\((I,j)\)
經過 Tiger.S 教授的勘察,收益矩陣 \(A,B,C\) 都已經知道了。你能幫 GDOI 求出一個收益最大的方案麽?
分析
網絡流是不可能網絡流的, 這輩子都不可能網絡流的.
我發現有 \(2^{nm}\) 種解, 然而答案範圍只有 \(1000nm\), 說明有大量的重復解, 那還寫正解?
這道題我用模擬退火, 隨機的是一個矩陣代表每個地方是什麽區域.
每次隨機一個點做一些微小的變化就可以了.
(如果調參調不下去可以試試多退火幾次, 跳出局部最優)
比如我是這樣寫的:
int ans = 0;
for(int i = 0; i < 42; i++)
ans = std::max(SA(), ans);
printf("%d", ans);
代碼
(不保證每時每刻你提交這份都能 AC)
#include <bits/stdc++.h> const int kMaxSize = 105, mod = 1e9 + 7; const double delta = 0.994, sup = 1e17, eps = 1e-17; bool plan[kMaxSize][kMaxSize]; int n, m, a[kMaxSize][kMaxSize], b[kMaxSize][kMaxSize], c[kMaxSize][kMaxSize]; unsigned sed = time(NULL); inline unsigned Rand() { sed = ((sed * 0x3abcd1ac + 0xabc12ab2) ^ (sed + 0x1230bace)) % mod; return sed; } int GetIncome() { int ans = 0; for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) { if(plan[i][j]) ans += a[i][j]; else ans += b[i][j]; if(i - 1 >= 0 && plan[i - 1][j] != plan[i][j]) ans += c[i][j]; if(j - 1 >= 0 && plan[i][j - 1] != plan[i][j]) ans += c[i][j]; if(i + 1 < n && plan[i + 1][j] != plan[i][j]) ans += c[i][j]; if(j + 1 < m && plan[i][j + 1] != plan[i][j]) ans += c[i][j]; } return ans; } inline int change(int ans, int x, int y) { plan[x][y] ^= 1; ans += plan[x][y] ? a[x][y] - b[x][y] : b[x][y] - a[x][y]; if(x - 1 >= 0) { ans += plan[x - 1][y] != plan[x][y] ? c[x][y] + c[x - 1][y] : -(c[x][y] + c[x - 1][y]); } if(y - 1 >= 0) { ans += plan[x][y - 1] != plan[x][y] ? c[x][y] + c[x][y - 1] : -(c[x][y] + c[x][y - 1]); } if(x + 1 < n) { ans += plan[x + 1][y] != plan[x][y] ? c[x][y] + c[x + 1][y] : -(c[x][y] + c[x + 1][y]); } if(y + 1 < m) { ans += plan[x][y + 1] != plan[x][y] ? c[x][y] + c[x][y + 1] : -(c[x][y] + c[x][y + 1]); } return ans; } int SA() { register int ans, old_ans, new_ans, cnt = 0; ans = old_ans = GetIncome(); for(register double T = sup; T > eps; T *= delta) { int x = Rand() % n, y = Rand() % m; new_ans = change(old_ans, x, y); ans = new_ans > ans ? new_ans : ans; if(new_ans > old_ans || Rand() <= exp((new_ans - old_ans) * 1.0 / T) * mod) old_ans = new_ans; else plan[x][y] ^= 1; cnt++; } return ans; } int main() { srand(time(NULL)); scanf("%d%d", &n, &m); for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) scanf("%d", &a[i][j]); for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) scanf("%d", &b[i][j]); for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) scanf("%d", &c[i][j]); for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) plan[i][j] = a[i][j] > b[i][j]; int ans = 0; for(int i = 0; i < 42; i++) ans = std::max(SA(), ans); printf("%d", ans); return 0; }
結語
不過, Luogu的數據貌似有點水, 這個代碼在一些地方貌似過不了?
【Luogu P1935】[國家集訓隊]圈地計劃