读完题发现拐角处一定放在 i + j i+j i+j为奇数的地方
所以可以想办法转化为 3 个 格 子 凑 成 L 形 状 权 值 的 最 大 值 3个格子凑成L形状权值的最大值 3个格子凑成L形状权值的最大值(因为要减去最多嘛)
然后就有一些性质了
当确定某个格子 ( i , j ) (i,j) (i,j)作为拐角放置
为了构成 L L L形状,还需要覆盖 2 2 2个格子
可以是上左,上右,下左,下右
发 现 这 4 个 格 子 的 i + j 都 是 偶 数 发现这4个格子的i+j都是偶数 发现这4个格子的i+j都是偶数
那 我 们 可 以 根 据 二 分 图 对 i + j 的 奇 偶 性 染 色 那我们可以根据二分图对i+j的奇偶性染色 那我们可以根据二分图对i+j的奇偶性染色
但这样不够
构 成 L 形 , 相 当 于 有 一 条 路 径 从 L 的 一 端 出 发 经 过 拐 点 到 达 另 一 个 格 子 构成L形,相当于有一条路径从L的一端出发经过拐点到达另一个格子 构成L形,相当于有一条路径从L的一端出发经过拐点到达另一个格子
价 值 是 拐 点 的 点 权 价值是拐点的点权 价值是拐点的点权
现 在 问 题 来 了 , L 的 两 段 i + j 都 是 偶 数 , 但 他 们 又 需 要 满 足 能 彼 此 匹 配 现在问题来了,L的两段i+j都是偶数,但他们又需要满足能彼此匹配 现在问题来了,L的两段i+j都是偶数,但他们又需要满足能彼此匹配
可 以 发 现 , L 两 段 虽 然 i + j 都 是 偶 数 , 但 所 处 的 j 一 个 是 奇 数 , 一 个 是 偶 数 可以发现,L两段虽然i+j都是偶数,但所处的j一个是奇数,一个是偶数 可以发现,L两段虽然i+j都是偶数,但所处的j一个是奇数,一个是偶数
所以至此就把点分成了 3 3 3类
L L L的构成可以看作从奇数列的偶数格子出发经过拐点到达偶数列的偶数格子
这样就可以构成一个三分图(也可以说四分图,因为拐点要被拆点了)
i + j 为 奇 数 的 拐 点 拆 为 i 和 i ′ , i 向 i ′ 连 一 条 流 量 1 , 费 用 为 点 权 的 边 i+j为奇数的拐点拆为i和i',i向i'连一条流量1,费用为点权的边 i+j为奇数的拐点拆为i和i′,i向i′连一条流量1,费用为点权的边
然 后 对 于 偶 数 格 子 , 只 能 向 部 分 相 邻 格 子 连 边 , 比 如 然后对于偶数格子,只能向部分相邻格子连边,比如 然后对于偶数格子,只能向部分相邻格子连边,比如
源 点 s 连 向 奇 数 列 的 偶 数 格 子 , 奇 数 列 的 偶 数 格 子 连 向 拐 点 的 i 源点s连向奇数列的偶数格子,奇数列的偶数格子连向拐点的i 源点s连向奇数列的偶数格子,奇数列的偶数格子连向拐点的i
i ′ 连 向 偶 数 列 的 偶 数 格 子 , 偶 数 列 的 偶 数 格 子 连 向 汇 点 t i'连向偶数列的偶数格子,偶数列的偶数格子连向汇点t i′连向偶数列的偶数格子,偶数列的偶数格子连向汇点t
(无特别说明,流量都是1,费用都是0)
#include
using namespace std;
const int maxn=50009;
const int inf=1e9;
#define id(x,y) (x-1)*n+y
int a[509][509];
int n,m,s,t,k;
int maxflow,mincost;
int dis[maxn],head[maxn<<1],cnt=1,flow[maxn],pre[maxn],vis[maxn];
struct edge{
int to,nxt,w,flow;//分别代表
}d[maxn<<1];
void add(int u,int v,int flow,int w)//最大流量,单位费用
{
d[++cnt]=(edge){v,head[u],w,flow},head[u]=cnt;
d[++cnt]=(edge){u,head[v],-w,0},head[v]=cnt;
}
bool spfa()
{
queueq;
for(int i=0;i<=t;i++) dis[i]=inf;
memset(vis,0,sizeof(vis));
q.push(s);
dis[s]=0,vis[s]=1;
flow[s] = inf;//初始流量无限大
while( !q.empty() )
{
int u=q.front(); q.pop();
vis[u]=0;//出队
for(int i=head[u];i;i=d[i].nxt)
{
if( !d[i].flow ) continue;//无流量了
int v=d[i].to;
if( dis[v]>dis[u]+d[i].w )
{
dis[v]=dis[u]+d[i].w;
flow[v] = min(flow[u],d[i].flow);//更新当前流量
pre[v]=i;//记录从哪条边过来的
if( !vis[v] ) vis[v]=1,q.push(v);
}
}
}
if( dis[t]==inf ) return 0;
return 1;
}
int x[5]={0,0,1,-1},y[5]={1,-1,0,0};
int main()
{
cin >> n >> m >> k;
int sumn=0;
s=0,t=n*n*2+1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin >> a[i][j],sumn+=a[i][j];
for(int i=1;i<=k;i++)
{
int l,r; cin >> l >> r;
a[l][r]=-1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if( a[i][j]==-1 ) continue;
if( (i+j)%2 )//奇数格子
add( id(i,j),id(i,j)+n*n,1,-a[i][j] );
else//偶数格子
{
if( j%2==1 )//奇数列
{
add(s,id(i,j),1,0);
for(int q=0;q<4;q++)
{
int nx=i+x[q],ny=j+y[q];
if( nx<1||nx>n||ny<1||ny>n ) continue;
if( a[nx][ny]==-1 ) continue;
add(id(i,j),id(nx,ny),1,0);
}
}
else
{
add(id(i,j),t,1,0);
for(int q=0;q<4;q++)
{
int nx=i+x[q],ny=j+y[q];
if( nx<1||nx>n||ny<1||ny>n ) continue;
if( a[nx][ny]==-1 ) continue;
add(id(nx,ny)+n*n,id(i,j),1,0);
}
}
}
}
while( spfa()&&m )
{
if( dis[t]>0 ) break;
int i,x=t;
while( x!=s )
{
i=pre[x];
d[i].flow-=flow[t],d[i^1].flow+=flow[t];
x=d[i^1].to;
}
m-=flow[t];
mincost+=dis[t]*flow[t];
}
cout << sumn+mincost;
}