P3356 火星探险问题
Sol
考虑建模:
考虑把每个点拆成 \((i,j)_1\) 和 \((i,j)_2\),分别表示入点和出点。(此处拆点是为了添加每个点上的岩石被选一次的限制。)
那么此时就要跑最大费用最大流,考虑把边权取反,那么就变成了常规的最小费用最大流。
- 对于 \(g_{i,j}\neq 1\),连一条 \((i,j)_1\to (i,j)_2\) 的边,流量 \(+\infty\),费用 \(0\)。
- 对于 \(g_{i,j}= 2\),连一条 \((i,j)_1\to (i,j)_2\) 的边,流量 \(1\),费用 \(-1\)。表示每个岩石至多被选 \(1\) 次。
- 对于 \((i,j)\) 满足 \(i<n\),连一条 \((i,j)_2\to (i+1,j)_1\) 的边,流量 \(+\infty\),费用 \(0\)。
- 对于 \((i,j)\) 满足 \(j<m\),连一条 \((i,j)_2\to (i,j+1)_1\) 的边,流量 \(+\infty\),费用 \(0\)。
- 连一条 \(s\to (1,1)_1\) 的边,流量 \(+\infty\),费用 \(0\)。
- 连一条 \((n,m)_2\to t\) 的边,流量 \(+\infty\),费用 \(0\)。
对于方案的处理,尽可能地往右,然后往下,这样考虑的方案一定和网络流的方案相等。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define pb push_back
#define pf push_front
#define desktop "C:\\Users\\incra\\Desktop\\"
#define IOS ios :: sync_with_stdio (false),cin.tie (0),cout.tie (0)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair <int,int> PII;
const int dx[] = {1,0,-1,0},dy[] = {0,-1,0,1};
template <typename T1,typename T2> bool tomax (T1 &x,T2 y) {
if (y > x) return x = y,true;
return false;
}
template <typename T1,typename T2> bool tomin (T1 &x,T2 y) {
if (y < x) return x = y,true;
return false;
}
LL power (LL a,LL b,LL p) {
LL ans = 1;
while (b) {
if (b & 1) ans = ans * a % p;
a = a * a % p;
b >>= 1;
}
return ans;
}
int fastio = (IOS,0);
#define endl '\n'
#define puts(s) cout << (s) << endl
const int N = 1000010,N1 = 40,M = 1000010,INF = 1e8;
int k,m,n,s,t;
int g[N1][N1];
int h[N],h2[N],e[M],ne[M],w[M],f[M],idx;
int d[N],flow[N],q[N],pre[N];
bool st[N];
int get (int x,int y) {
return (x - 1) * m + y;
}
void add_edge_ (int h[],int a,int b,int c,int d) {
e[idx] = b;
f[idx] = c;
w[idx] = d;
ne[idx] = h[a];
h[a] = idx++;
}
void add_edge (int h[],int a,int b,int c,int d) {
add_edge_ (h,a,b,c,d),add_edge_ (h,b,a,0,-d);
}
bool SPFA () {
int hh = 0,tt = 0;
q[tt++] = s;
memset (d,0x3f,sizeof (d));
memset (flow,0,sizeof (flow));
d[s] = 0,flow[s] = INF;
while (hh != tt) {
int u = q[hh++];
if (hh == N) hh = 0;
st[u] = 0;
for (int i = h[u];~i;i = ne[i]) {
int v = e[i];
if (f[i] && d[v] > d[u] + w[i]) {
d[v] = d[u] + w[i];
pre[v] = i;
flow[v] = min (flow[u],f[i]);
if (!st[v]) {
q[tt++] = v;
if (tt == N) tt = 0;
st[v] = 1;
}
}
}
}
return flow[t] > 0;
}
void EK (int &ans1,int &ans2) {
ans1 = ans2 = 0;
while (SPFA ()) {
int tmp = flow[t];
ans1 += tmp,ans2 += tmp * d[t];
for (int i = t;i != s;i = e[pre[i] ^ 1]) f[pre[i]] -= tmp,f[pre[i] ^ 1] += tmp;
}
}
void print (int id,int u) {
for (int i = h2[u];~i;i = ne[i]) {
int v = e[i];
if (!f[i]) continue;
f[i]--;
if (v == u + 1) cout << id << ' ' << 1 << endl;
else cout << id << ' ' << 0 << endl;
print (id,v);
return ;
}
}
void mian () {
memset (h,-1,sizeof (h));
memset (h2,-1,sizeof (h2));
cin >> k >> m >> n;
s = 2 * n * m + 1,t = 2 * n * m + 2;
add_edge (h,s,1,k,0),add_edge (h,2 * n * m,t,k,0);
for (int i = 1;i <= n;i++) for (int j = 1;j <= m;j++) {
cin >> g[i][j];
if (g[i][j] == 2) add_edge (h,get (i,j),get (i,j) + n * m,1,-1);
if (g[i][j] != 1) add_edge (h,get (i,j),get (i,j) + n * m,INF,0);
}
for (int i = 1;i <= n;i++) for (int j = 1;j <= m;j++) {
if (i + 1 <= n) add_edge (h,get (i,j) + n * m,get (i + 1,j),INF,0);
if (j + 1 <= m) add_edge (h,get (i,j) + n * m,get (i,j + 1),INF,0);
}
int ans1,ans2;
EK (ans1,ans2);
for (int i = n * m + 1;i <= 2 * n * m - 1;i++) {
if (g[(i - n * m - 1) / m + 1][(i - n * m - 1) % m + 1] == 1) continue;
for (int _ = h[i];~_;_ = ne[_]) {
int j = e[_];
if (j + n * m == i) continue;
if (f[_] == INF) continue;
if (j != t) add_edge (h2,i - n * m,j,INF - f[_],0);
}
}
for (int i = 1;i <= k;i++) print (i,1);
}
int main () {
int T = 1;
// cin >> T;
while (T--) mian ();
return 0;
}

浙公网安备 33010602011771号