LOJ传送门
洛谷传送门
解析:
考虑将所有点交替黑白染色,源点向黑点连边,白点向汇点连边。每个点在四周建立四个点,表示这个位置是否被连接,然后所有挨着的点连边,比如 i i i上面的点的下标点和 i i i的上标点连边, i i i左边点的右标点和 i i i的左标点连边。
现在我们需要考虑怎么连接 i i i和 i i i四周的点。
显然初始状态接触的点直接连接费用为 0 0 0的边。
现在对于可以旋转的点分情况考虑:
1.单插头类型:1,2,4,8
只讨论 1 1 1的情况,其他显然可以通过旋转得到。
i i i向上标点连费用为 0 0 0的边,然后上标点向左右连费用为 1 1 1的边,然后向下标点连费用为 2 2 2的边。
显然这样保证流量为 1 1 1且各个方向流过来的答案是正确的。
2.L型:3,6,12,9
只讨论 3 3 3,其他可以通过旋转得到。
i i i向上标和右标连费用为 0 0 0的点,然后考虑旋转的影响。
旋转一次,我们发现其实对于 L L L型来说相当于做一次轴对称,所以我们只需要将右标向左标连费用为 1 1 1的边,上标向下标连费用为 1 1 1的边,分别表示一次轴对称(旋转)的花费。
3.凸型:7,14,13,11
实际上和
1
1
1的情况极为相似,留给读者自行思考 (或者直接看代码)
跑出来判断一下流满没有,没有流满就无解。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc getchar
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
while(!isdigit(c=gc()));re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
std::ofstream fout;
using std::cerr;
using std::cout;
inline bool ckmin(int &a,int b){return a>b?(a=b,true):false;}
cs int N=1e4+4;
namespace NetWork{
struct edge{
int to,cap,cost,rev;
edge(cs int &_to,cs int &_cap,cs int &_cost,cs int &_rev):
to(_to),cap(_cap),cost(_cost),rev(_rev){}
};
std::vector<edge> G[N];
typedef std::vector<edge>::iterator iter;
iter cur[N];
inline void addedge(int u,int v,int cap,int cost,bool col=false){
if(col)std::swap(u,v);
G[u].push_back(edge(v,cap,cost,G[v].size()));
G[v].push_back(edge(u,0,-cost,G[u].size()-1));
}
int S,T;
int dist[N];
bool vis[N];
inline bool BFS(){
memset(dist,0x3f,sizeof(int)*(T+1));
dist[S]=0;
std::queue<int> q({S});
while(!q.empty()){
int u=q.front();q.pop();
vis[u]=false;
for(iter re e=G[u].begin();e!=G[u].end();++e){
if(e->cap&&dist[e->to]>dist[u]+e->cost){
dist[e->to]=dist[u]+e->cost;
if(!vis[e->to]){
vis[e->to]=true;
q.push(e->to);
}
}
}
}
return dist[T]<N;
}
inline int dfs(int u,cs int &flow){
if(u==T)return flow;
int ans=0;vis[u]=true;
for(iter &e=cur[u];e!=G[u].end();++e)if(e->cap&&dist[e->to]==dist[u]+e->cost&&!vis[e->to]){
int delta=dfs(e->to,std::min(flow-ans,e->cap));
if(delta){
e->cap-=delta;
G[e->to][e->rev].cap+=delta;
if((ans+=delta)==flow){
vis[u]=false;
return flow;
}
}
}
vis[u]=false;
return ans;
}
int mxflow,totcost;
inline void Flow(){
while(BFS()){
for(int re i=0;i<=T;++i)cur[i]=G[i].begin();
int tmp=dfs(S,0x3f3f3f3f);
mxflow+=tmp;
totcost+=tmp*dist[T];
}
}
}
using namespace NetWork;
int n,m,totflow;
signed main(){
n=getint(),m=getint();
S=0,T=n*m*5+1;
for(int re i=0,k=1;i<n;++i)
for(int re j=0;j<m;++j,++k){
int turn=0;
#define id(x) ((x)*5)
#define u(x) (((x)-1)*5+1+(turn&3))
#define r(x) (((x)-1)*5+1+((turn+1)&3))
#define d(x) (((x)-1)*5+1+((turn+2)&3))
#define l(x) (((x)-1)*5+1+((turn+3)&3))
bool col=(i+j)&1;
if(col)addedge(S,id(k),0x3f3f3f3f,0);
else addedge(id(k),T,0x3f3f3f3f,0);
if(i)addedge(d(k-m),u(k),1,0,col);
if(j)addedge(r(k-1),l(k),1,0,col);
int typ=getint();
if(typ&1)addedge(u(k),id(k),1,0,col),++totflow;
if(typ&2)addedge(r(k),id(k),1,0,col),++totflow;
if(typ&4)addedge(d(k),id(k),1,0,col),++totflow;
if(typ&8)addedge(l(k),id(k),1,0,col),++totflow;
switch(typ){
case 8:++turn;
case 4:++turn;
case 2:++turn;
case 1:
addedge(r(k),u(k),1,1,col);
addedge(d(k),u(k),1,2,col);
addedge(l(k),u(k),1,1,col);
break;
case 9:++turn;
case 12:++turn;
case 6:++turn;
case 3:
addedge(d(k),u(k),1,1,col);
addedge(l(k),r(k),1,1,col);
break;
case 11:++turn;
case 13:++turn;
case 14:++turn;
case 7:
addedge(l(k),r(k),1,2,col);
addedge(l(k),u(k),1,1,col);
addedge(l(k),d(k),1,1,col);
break;
}
}
Flow();
cout<<(mxflow*2==totflow?totcost:-1)<<"\n";
return 0;
}