虽然一眼看出是网络流,但是一直想不出正解。。想了好久。
实际上,将方向看成一条有向边,每个点能回到自己不就是要求原图是若干个强联通分量吗?于是由于每个点出度为1,因此入读也只能为1,然后搞一个二分图,左边表示出去的点,右边表示到的点,然后对于可以走到一对点,连边容量为1,费用为1(需要修改)或0(不需要修改)。跑完美匹配的最小权值,转化成最小费用最大流即可。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> #define inf 1000000000 #define N 4005 using namespace std; int n,m,tot=1,fst[N],pnt[N],len[N],cst[N],nxt[N]; int h[N],ps[25][25],sta,gol,d[N],fa[N],e[N],ans=0; bool bo[N]; char ch[N]; const int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1}; int exchg(char c){ if (c=='U') return 0; if (c=='D') return 1; if (c=='L') return 2; return 3; } void add(int aa,int bb,int cc,int dd){ pnt[++tot]=bb; len[tot]=cc; cst[tot]=dd; nxt[tot]=fst[aa]; fst[aa]=tot; } bool spfa(){ int head=0,tail=1,i; h[1]=sta; for (i=0; i<=gol; i++) d[i]=inf; d[sta]=0; memset(bo,1,sizeof(bo)); while (head!=tail){ head=head%4000+1; int x=h[head],p; bo[x]=1; for (p=fst[x]; p; p=nxt[p]) if (len[p]){ int y=pnt[p]; if (d[x]+cst[p]<d[y]){ d[y]=d[x]+cst[p]; fa[y]=x; e[y]=p; if (bo[y]){ bo[y]=0; tail=tail%4000+1; h[tail]=y; } } } } return d[gol]<inf; } void updata(){ int i,p,tmp=inf; for (i=gol; i!=sta; i=fa[i]){ p=e[i]; tmp=min(tmp,len[p]); } for (i=gol; i!=sta; i=fa[i]){ p=e[i]; ans+=tmp*cst[p]; len[p]-=tmp; len[p^1]+=tmp; } } int main(){ scanf("%d%d",&m,&n); int i,j,k,tmp=0; for (i=1; i<=m; i++) for (j=1; j<=n; j++) ps[i][j]=++tmp; for (i=1; i<=m; i++){ scanf("%s",ch+1); for (j=1; j<=n; j++){ int t=exchg(ch[j]); for (k=0; k<4; k++){ int x=i+dx[k],y=j+dy[k]; if (!x) x=m; if (x>m) x=1; if (!y) y=n; if (y>n) y=1; if (k==t){ add(ps[i][j],ps[x][y]+m*n,1,0); add(ps[x][y]+m*n,ps[i][j],0,0); } else{ add(ps[i][j],ps[x][y]+m*n,1,1); add(ps[x][y]+m*n,ps[i][j],0,-1); } } } } sta=0; gol=2*m*n+1; for (i=1; i<=m; i++) for (j=1; j<=n; j++){ add(sta,ps[i][j],1,0); add(ps[i][j],sta,0,0); add(ps[i][j]+m*n,gol,1,0); add(gol,ps[i][j]+m*n,0,0); } while (spfa()) updata(); printf("%d\n",ans); return 0; }