[at code festival 2016 J]Neue Spiel
每次操作可以看作将物品放到该方向上第一个无物品的位置
从操作向该行/列的位置连边,显然需存在完美匹配,并考虑一组匹配是否合法
限制即每个位置(操作时间)要比所匹配操作方向上之前的位置大,以此连边后无环即合法
当产生环时,考虑调整,将每个操作改为匹配其原来所匹配位置连向的位置
定义势能为总边数,如果每个点依次搜索出边,则恰会以与减少边数相同的代价找到该环
换言之,均摊为\(O(n^{3})\)(包括最终无环的判定),结合二分图匹配,时间复杂度为\(O(n^{3})\)
#include<bits/stdc++.h>
using namespace std;
const int N=305,M=100000;
int n,T,E,x,flag,head[M],Head[M],d[M],st[M],vis[M],r[M],ans[M];
queue<int>q;vector<int>V,e[M];
struct List{int nex,to,len;}edge[M*10];
void add(int x,int y,int z){
edge[E]=List{head[x],y,z},head[x]=E++;
edge[E]=List{head[y],x,0},head[y]=E++;
}
bool bfs(){
memset(d,-1,sizeof(d));
d[0]=0,q.push(0);
while (!q.empty()){
int k=q.front();q.pop();
for(int i=head[k];i!=-1;i=edge[i].nex)
if ((edge[i].len)&&(d[edge[i].to]<0)){
d[edge[i].to]=d[k]+1,q.push(edge[i].to);
}
}
return d[T]>=0;
}
int dfs(int k,int s){
if (k==T)return s;
int ans=0;
for(int &i=head[k];i!=-1;i=edge[i].nex)
if ((edge[i].len)&&(d[edge[i].to]==d[k]+1)){
int p=dfs(edge[i].to,min(s,edge[i].len));
edge[i].len-=p,edge[i^1].len+=p,s-=p,ans+=p;
if (!s)break;
}
return ans;
}
int dinic(){
int ans=0;
memcpy(Head,head,sizeof(head));
while (bfs()){
ans+=dfs(0,0x3f3f3f3f);
memcpy(head,Head,sizeof(head));
}
return ans;
}
int id(int x,int y){
return (x-1)*n+y+(n<<2);
}
int getx(int k){
return (k-(n<<2)-1)/n+1;
}
int gety(int k){
return (k-(n<<2)-1)%n+1;
}
void write(int k){
if (ans[k]==0)printf("U%d\n",gety(k));
if (ans[k]==1)printf("D%d\n",gety(k));
if (ans[k]==2)printf("L%d\n",getx(k));
if (ans[k]==3)printf("R%d\n",getx(k));
}
void dfs(int k);
bool work(int k,int i){
if (!vis[i])dfs(i);
else{
if (vis[i]==1){
int s=ans[k];
while (st[st[0]]!=i){
ans[st[st[0]]]=ans[st[st[0]-1]];
vis[st[st[0]--]]=0;
}
ans[i]=s,st[0]--,dfs(i);
return 1;
}
}
return vis[k]!=1;
}
void dfs(int k){
int x=getx(k),y=gety(k);
st[++st[0]]=k,vis[k]=1;
if (ans[k]==0){
while (--x){
if (work(k,id(x,y)))return;
}
}
if (ans[k]==1){
while (++x<=n){
if (work(k,id(x,y)))return;
}
}
if (ans[k]==2){
while (--y){
if (work(k,id(x,y)))return;
}
}
if (ans[k]==3){
while (++y<=n){
if (work(k,id(x,y)))return;
}
}
st[0]--,vis[k]=2;
}
int main(){
scanf("%d",&n),T=id(n,n)+1;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)V.push_back(id(i,j));
for(int i=1;i<=n;i++){
scanf("%d",&x),add(0,i,x);
for(int j=1;j<=n;j++)add(i,id(j,i),1);
}
for(int i=1;i<=n;i++){
scanf("%d",&x),add(0,i+n,x);
for(int j=1;j<=n;j++)add(i+n,id(j,i),1);
}
for(int i=1;i<=n;i++){
scanf("%d",&x),add(0,i+(n<<1),x);
for(int j=1;j<=n;j++)add(i+(n<<1),id(i,j),1);
}
for(int i=1;i<=n;i++){
scanf("%d",&x),add(0,i+n*3,x);
for(int j=1;j<=n;j++)add(i+n*3,id(i,j),1);
}
for(int i:V)add(i,T,1);
if (dinic()!=n*n){
printf("NO\n");
return 0;
}
for(int I=0;I<4;I++)
for(int i=1;i<=n;i++)
for(int j=head[i+n*I];j!=-1;j=edge[j].nex)
if ((edge[j].to)&&(!edge[j].len))ans[edge[j].to]=I;
for(int i:V)
if (!vis[i])dfs(i);
for(int i:V){
int x=getx(i),y=gety(i);
if (ans[i]==0){
while (--x)r[i]++,e[id(x,y)].push_back(i);
}
if (ans[i]==1){
while (++x<=n)r[i]++,e[id(x,y)].push_back(i);
}
if (ans[i]==2){
while (--y)r[i]++,e[id(x,y)].push_back(i);
}
if (ans[i]==3){
while (++y<=n)r[i]++,e[id(x,y)].push_back(i);
}
if (!r[i])q.push(i);
}
while (!q.empty()){
int k=q.front();q.pop();
write(k);
for(int i:e[k])
if (--r[i]==0)q.push(i);
}
return 0;
}