题解 marshland
是个最大费用可行流
这题的建边很毒瘤
首先有危险度的点肯定要拆点
关键在于其它点怎么办
如果拆了不好保证每个点只经过一次
不拆连网络流都跑不了
但仔细观察题面,不能不难(???)发现一个L中那两个坐标和为偶数的点一定分两种
(奇, 奇)和(偶, 偶)
那可以用这个性质建边,一类连源点,一类连汇点就行了
- 所以总结是啥?坐标题善于发现坐标的奇偶性规律?
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 5000
#define ll long long
#define fir first
#define sec second
#define make make_pair
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m, k;
int head[N], size=1, val[55][55], tot, s1, s2, t, dis[N], inc[N], back[N], ans, sum;
bool lim[55][55], vis[N];
pair<int, int> mp[55][55];
struct edge{int to, next, flw, cst;}e[N<<3];
inline void add(int s, int t, int f, int c) {e[++size].to=t; e[size].flw=f; e[size].cst=c; e[size].next=head[s]; head[s]=size;}
bool spfa(int s, int t) {
//cout<<"spfa "<<s<<' '<<t<<endl;
memset(dis, 127, sizeof(dis));
memset(back, -1, sizeof(back));
dis[s]=0; inc[s]=INF;
queue<int> q;
q.push(s);
int u;
while (q.size()) {
u=q.front(); q.pop();
vis[u]=0;
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (e[i].flw && dis[v]>dis[u]+e[i].cst) {
dis[v]=dis[u]+e[i].cst;
//cout<<"dis: "<<dis[v]<<endl;
back[v]=i; inc[v]=min(inc[u], e[i].flw);
if (!vis[v]) q.push(v), vis[v]=1;
}
}
}
return ~back[t];
}
signed main()
{
memset(head, -1, sizeof(head));
n=read(); m=read(); k=read();
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j)
val[i][j]=read(), sum+=val[i][j]; //, cout<<val[i][j]<<endl;
for (int i=1,x,y; i<=k; ++i) {
x=read(); y=read();
lim[x][y]=1;
}
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j)
if (!lim[i][j]) {
if ((i+j)&1) mp[i][j]=make(tot+1, tot+2), tot+=2;
else mp[i][j]=make(++tot, 0);
}
s1=++tot, s2=++tot, t=++tot;
//cout<<"s: "<<s1<<' '<<s2<<' '<<t<<endl;
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j) if (!lim[i][j]) {
if ((i+j)&1) {
add(mp[i][j].fir, mp[i][j].sec, 1, -val[i][j]), add(mp[i][j].sec, mp[i][j].fir, 0, val[i][j]);
}
else {
if (i&1) {
add(s2, mp[i][j].fir, 1, 0), add(mp[i][j].fir, s2, 0, 0);
if (i<n && !lim[i+1][j]) add(mp[i][j].fir, mp[i+1][j].fir, 1, 0), add(mp[i+1][j].fir, mp[i][j].fir, 0, 0);
if (j<n && !lim[i][j+1]) add(mp[i][j].fir, mp[i][j+1].fir, 1, 0), add(mp[i][j+1].fir, mp[i][j].fir, 0, 0);
if (i>1 && !lim[i-1][j]) add(mp[i][j].fir, mp[i-1][j].fir, 1, 0), add(mp[i-1][j].fir, mp[i][j].fir, 0, 0);
if (j>1 && !lim[i][j-1]) add(mp[i][j].fir, mp[i][j-1].fir, 1, 0), add(mp[i][j-1].fir, mp[i][j].fir, 0, 0);
}
else {
add(mp[i][j].fir, t, 1, 0), add(t, mp[i][j].fir, 0, 0);
if (i>1 && !lim[i-1][j]) add(mp[i-1][j].sec, mp[i][j].fir, 1, 0), add(mp[i][j].fir, mp[i-1][j].sec, 0, 0);
if (j>1 && !lim[i][j-1]) add(mp[i][j-1].sec, mp[i][j].fir, 1, 0), add(mp[i][j].fir, mp[i][j-1].sec, 0, 0);
if (i<n && !lim[i+1][j]) add(mp[i+1][j].sec, mp[i][j].fir, 1, 0), add(mp[i][j].fir, mp[i+1][j].sec, 0, 0);
if (j<n && !lim[i][j+1]) add(mp[i][j+1].sec, mp[i][j].fir, 1, 0), add(mp[i][j].fir, mp[i][j+1].sec, 0, 0);
}
}
}
add(s1, s2, 0, 0), add(s2, s1, 0, 0);
//cout<<"pos1"<<endl;
int tem=0;
for (int i=1,it=size-1; i<=m; ++i) {
++e[it].flw;
while (spfa(s1, t)) {
tem+=dis[t];
ans=min(ans, tem);
//cout<<"ans: "<<ans<<endl;
for (int u=t; u!=s1; u=e[back[u]^1].to) {
e[back[u]].flw-=inc[t];
e[back[u]^1].flw+=inc[t];
}
}
}
printf("%d\n", sum+ans);
return 0;
}