题意
自己看题面
Sol
一个格子只能被占用一次,n只有50?
有什么想法?
DP? 退火? 贪心?
先来看看这个柱子是个啥玩意
首先只用把拐角出放在一个X+Y是奇数的格子
然后他会占掉另外两个格子,并且是它周围的相邻的格子
发现一个东西:占掉的一定是一个X和Y均是偶数的格子和一个X和Y均是奇数的格子
把这两个分开考虑? 感觉放了一个柱子就等价于走了一遍上面提到过的三种格子
这样一个网络流的模型就出来了,由于有权值是费用流
你不觉得是三分图最大权匹配?
我们做过三分图最大匹配,显然就把中间那一列每一个点拆为两个点,正好还能很好地体现不稳定度的概念,看出来每增广一次就是放了一个柱子
那么建图就比较显然了
第一列到第二列是如果点是相邻的则连容量为 1 费用为 0 的边,第三列到第四列同理,第二列到第三列的相同点则连容量为 1 费用为危险度的边,然后源点向第一列,第四列向汇点连容量
为 1 费用为 0 的边。
然后跑最大费用最大流,当此次增广的费用是负的了或者增广了m次时就 break 。如果你没有判断增广的费用是否为负的话 就会WA得很惨,因为可能根本就放不下 个石头,后面增广的费用是为了得到最大流而退流形成的,并不需要石头越多越好,我们只希望费用最大。
P.S. : 这道题建图其实并不是很难,但要想到点子上,把点分好类
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<queue>
#define Set(a,b) memset(a,b,sizeof(a))
using namespace std;
inline int read(){
int x=0;char ch=getchar();int t=1;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
return x*t;
}
bool vis[51][51];
int id[51][51];
int val[51][51];
typedef long long ll;
const int INF=2147483637;
int n,m,k;int cnt=0;
const int dx[5]={0,-1,0,1,0};
const int dy[5]={0,0,1,0,-1};
#define CK(a,b) (a>0&&b>0&&a<=n&&b<=n&&!vis[a][b])
namespace EK{
const int N=51;
const int MAXN=5000;
typedef long long ll;
typedef pair<int,int> Pr;
struct edge{//网络流建图
int to,next,w,cap;
}a[50000];
int head[MAXN];int cnt=0;int cn=0;
int id[N][N];int id2[N][N];
int S=0,T;int ans=0;
inline void add(int x,int y,int cap,int w){
a[cn]=(edge){y,head[x],w,cap};head[x]=cn++;
}
void Build_Graph(){
T=++cnt;Set(head,-1);
for(register int i=1;i<=n;++i)
for(register int j=1;j<=n;++j){
if(vis[i][j]) continue;
register int x,y;
if(val[i][j]) {
add(id[i][j],id2[i][j],1,val[i][j]);add(id2[i][j],id[i][j],0,-val[i][j]);
for(register int k=1;k<=4;++k){
x=i+dx[k],y=j+dy[k];
if((x&1)||(y&1)) continue;//中间点向偶偶点
if(!CK(x,y)) continue;
add(id2[i][j],id[x][y],1,0);add(id[x][y],id2[i][j],0,0);
}
}
else{
if((i&1)&&(j&1)){//奇奇
add(S,id[i][j],1,0);add(id[i][j],S,0,0);
for(register int k=1;k<=4;++k){
x=i+dx[k],y=j+dy[k];
if(!val[x][y]) continue;if(!CK(x,y)) continue;//奇奇点向奇点连边
add(id[i][j],id[x][y],1,0);add(id[x][y],id[i][j],0,0);
}
}
else if((!(i&1))&&(!(j&1))){//偶偶
add(id[i][j],T,1,0);add(T,id[i][j],0,0);//只要向汇点连边就行了
}
}
}
return ;
}
int pre_ed[MAXN];int dis[MAXN];bool in[MAXN];
queue<int> Q;
bool spfa(){
while(!Q.empty()) Q.pop();
Q.push(S);in[S]=1;Set(dis,-127/3);dis[S]=0;register int NO=dis[1];
Set(pre_ed,-1);
while(!Q.empty()){
register int u=Q.front();Q.pop();
for(register int v,i=head[u];~i;i=a[i].next){
v=a[i].to;
if(a[i].cap<=0) continue;
if(dis[v]<dis[u]+a[i].w){
dis[v]=dis[u]+a[i].w;pre_ed[v]=i^1;
if(!in[v]) Q.push(v),in[v]=1;
}
}
in[u]=0;
}
if(dis[T]==NO||dis[T]<=0) return 0;
ans-=dis[T];
register int flow=2147483647;
register int u=T;
while(u!=S) flow=min(flow,a[pre_ed[u]^1].cap),u=a[pre_ed[u]].to;
for(u=T;u!=S;u=a[pre_ed[u]].to){
register int pre=pre_ed[u];
a[pre^1].cap-=flow;a[pre].cap+=flow;
}
return 1;
}
void work(){
while(spfa()){
--m;if(!m) break;
}
printf("%d\n",ans);
}
}
int main()
{
n=read();m=read();k=read();
for(register int i=1;i<=n;++i) for(register int j=1;j<=n;++j) {
val[i][j]=read(),EK::id[i][j]=++EK::cnt;EK::ans+=val[i][j];
if(val[i][j]!=0) EK::id2[i][j]=++EK::cnt;
}
register int x,y;
for(register int i=1;i<=k;++i) {x=read();y=read();vis[x][y]=1;}
EK::Build_Graph();
EK::work();
}