题解 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;
}
posted @ 2021-08-17 20:35  Administrator-09  阅读(12)  评论(0编辑  收藏  举报