题面
n<=50
巧妙构图
想了好久流还是不会
注意题目给的奇偶性质 : 一个L中间必定X+Y=奇数。
显然L除了中间之外的旁边的两个点一定是X+Y=偶数的。
仔细观察bing就bu能发现,这两个点一个是偶+偶,一个是奇+奇。
根据这个构图,就变成了简单的最大费用可行流。
别忘了最大流量限制为m.
关于可行流,有一个结论是,当最长路小于0时,不再进行增广。
感性理解一下,不会由于当前的负增广而出现更大的正增广。
费用流采用的是多路增广,避免重点的方法。
实现
#include <cstdio>
#include <iostream>
#include <cstring>
#define min(a,b) ((a)>(b)?(b):(a))
using namespace std;
typedef long long ll;
const ll N = 55, E = N * N * 10;
ll n,m,k,dan[N][N],id[N][N],vt,wid[N][N];
ll final[N * N * 2], S, T, to[E], nex[E], cost[E], f[E], tot, ban[N * N * 2];
ll s,SS;
void link0(ll x,ll y,ll fl,ll w) {
to[++tot] = y,nex[tot] = final[x],final[x]=tot;
cost[tot] = w,f[tot] = fl;
}
void link(ll x,ll y,ll fl,ll w) {
if (!x||!y||ban[x]||ban[y]) return;
link0(x,y,fl,w);
link0(y,x,0,-w);
}
ll Q[N * N * 20], dis[N * N * 2], vis[N * N * 2],step[N * N * 2];
void spfa() {
ll L = 0, R = 0; Q[R = 1] = SS;
memset(dis,128,sizeof dis); dis[SS] = 0;
while (L < R) {
ll x = Q[++L]; vis[x] = 0;
for (ll i = final[x]; i; i=nex[i]) if (f[i]) {
ll y = to[i];
if (cost[i] + dis[x] > dis[y]) {
dis[y] = cost[i] + dis[x];
if (!vis[y])
Q[++R] = y, vis[y] = 1;
}
}
}
}
ll go(ll x,ll wait) {
if (x == T)
return wait;
ll used = 0; vis[x] = 1;
for (ll i = final[x]; i; i=nex[i])
if (f[i] && dis[to[i]] == dis[x] + cost[i]) {
ll y = to[i];
if (!vis[y]) {
ll take = go(y, min(wait - used, f[i]));
s -= cost[i] * take;
f[i] -= take, f[i^1] += take;
used += take;
if (used == wait) break;
}
}
vis[x] = 0;
return used;
}
int main() {
freopen("marshland.in","r",stdin);
// freopen("marshland.out","w",stdout);
tot = 1;
cin>>n>>m>>k;
for (ll i = 1; i <= n; i++) for (ll j = 1; j <= n; j++) {
scanf("%lld",&dan[i][j]), id[i][j] = ++vt;
s+=dan[i][j];
if ((i+j)&1) wid[i][j] = ++vt;
}
for (ll i = 1; i <= k; i++) {
ll x,y; scanf("%lld %lld",&x,&y);
ban[id[x][y]]=1;
}
S=vt+1,T=vt+2,SS=vt+3;
link(SS,S,1e9,0);
for (ll i = 1; i <= n; i+=2) for (ll j = 1; j <= n; j+=2)
link(S, id[i][j], 1, 0);
for (ll i = 2; i <= n; i+=2) for (ll j = 2; j <= n; j+=2)
link(id[i][j], T, 1, 0);
for (ll i = 1; i <= n; i+=2) for (ll j = 2; j <= n; j+=2) {
link(id[i][j], wid[i][j], 1, dan[i][j]);
link(id[i][j-1], id[i][j], 1, 0);
link(id[i][j+1], id[i][j], 1, 0);
link(wid[i][j], id[i-1][j], 1, 0);
link(wid[i][j], id[i+1][j], 1, 0);
}
for (ll i = 2; i <= n; i+=2) for (ll j = 1; j <= n; j+=2) {
link(id[i][j], wid[i][j], 1, dan[i][j]);
link(wid[i][j], id[i][j-1], 1, 0);
link(wid[i][j], id[i][j+1], 1, 0);
link(id[i-1][j], id[i][j], 1, 0);
link(id[i+1][j], id[i][j], 1, 0);
}
int cnt = 0;
while (spfa(), 1) {
cnt++;
go(SS, 1e9);
if (cnt == 517) {
int kkk = cnt;
}
}
cout<<s<<endl;
}