很妙的一道题
对于每个格子,它合法与否,只跟它上下左右的相邻格子有关,所以可以想到黑白染色
(用 (i,j) 表示 i 行 j 列的格子,我把 (i+j) %2 == 0 的格子染成白色,把(i+j)%2 == 1 的格子染成黑色)
关键是怎么描述旋转操作
我朴实的想法是给每个格子建四个点,每个点代表格子可通过旋转达到的一种状态,从格子的初始状态向格子其它状态连边,然后把格子间可以匹配的状态连起来,再然后……就没有然后了
看了题解,真的被惊到了
还是把每个格子拆成4个节点,对应四个方向上的接口
从源点向白格的接口连流量上界1,费用0的边
从黑格的接口向汇点连流量上界1,费用0的边
从黑格的接口向可以匹配的白格的接口连流量上界1,费用0的边
然后旋转操作,就可以通过一种神奇方式描述出来:
A
D O B
C
把这个看成一个白格(黑格类似,只是连边方向相反)
边(u->v,f,w)代表从 u 到 v,流量上界 f,费用 w 的边
下文描述的旋转方向均为顺时针
1.此格有1个接口:
A
|
D O B
C
(A->B ,1,1)) 对应转90度
(A->C, 1,2)) 对应转180度
(A->D ,1,1)) 对应转270度
2.此格有2个接口:
情况1:
A
|
D O —— B
C
(A->C, 1,1)) 对应转90度
(B->D ,1,1)) 对应转270度
(A->C, 1,1))+(B->D ,1,1)) 对应转180度
情况2:
A
|
D O B
|
C
不能转,忽略
3.此格有3个接口:
A
|
D O —— B
|
C
(A->D ,1,1)) 对应转270度
(B->D, 1,2)) 对应转180度
(C->D ,1,1)) 对应转90度
4.此格有4个接口:
A
|
D —— O —— B
|
C
转了也没区别,忽略
建完图后跑最小费用最大流即可
如果我表达不清的话,这是样例1的建图,很丑,凑合着看吧
图上标的数代表费用,没标的边默认费用为0,所有边的流量上界默认为1
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int inf=0x7fffffff;
const int N=8100;
const int M=200010;
struct Edge{
int u,v,f,w,nxt;
}edge[M<<1];
int s,t,head[N],cnt,maxflow,mincost,dis[N],inque[N],pre[N];
queue<int> q;
int n,m,id[2005][2005][4],num,gr[2005][2005],tot;
void add(int u,int v,int f,int w){
edge[cnt].u=u;edge[cnt].v=v;edge[cnt].f=f;edge[cnt].w=w;edge[cnt].nxt=head[u];head[u]=cnt++;
edge[cnt].u=v;edge[cnt].v=u;edge[cnt].f=0;edge[cnt].w=-w;edge[cnt].nxt=head[v];head[v]=cnt++;
}
bool spfa(){
memset(dis,0x7f,sizeof(dis));
memset(inque,0,sizeof(inque));
memset(pre,-1,sizeof(pre));
dis[s]=0;
q.push(s);inque[s]=1;