Codeforces 1416F. Showing Off 题解

题目链接:F. Showing Off

题目大意:洛谷


题解:如果将格子之间按方向连边,因为不允许走出边界,所以最后一定是若干个内向基环树。

容易发现环上的点的权值和一定一样大。

因为环的大小无所谓,本题中又显然不存在环长为奇数环,所以我们选定所有环长为 2 肯定不劣。

接下来考虑非环边,一个点能够存在非环边自然周围四格有至少一个格子的权值比它小,所以这些点可以不必处在环上,但是如果一个点周围四格都不比它小,那么这个点要强制选择。

如果没有强制选择的限制那么这一道题就变成了骨牌覆盖问题直接黑白染色建二分图跑网络流就可以了。

有了强制选择我们可以令这些边的边权变为 1 ,然后对于整张图跑最大费用最大流就结束了。

如果跑出来的代价不对直接输出无解就好了。

其实费用流应该改成有上下界的网络流才对,但是这题费用流跑得飞快我也就没管了。

时间复杂度不会证。

代码:

#include <cstdio>
#include <cstring>
void read(int &a){
	a=0;
	char c=getchar();
	while(c<'0'||c>'9'){
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		a=(a<<1)+(a<<3)+(c^48);
		c=getchar();
	}
}
template<typename Elem_1,typename Elem_2>
Elem_1 min(Elem_1 a,Elem_2 b){
	return a<b?a:b;
}
const int Maxn=100000;
const int Maxm=1000000;
const int Inf=0x3f3f3f3f;
const int d[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
char D[4]={'R','L','D','U'};
struct Edge{
	int to,nxt,val;
	int cap,flow;
}edge[Maxm<<1|5];
int head[Maxn+5],tot;
void unuse_add_edge(int from,int to,int cap,int val){
	edge[++tot].to=to;
	edge[tot].nxt=head[from];
	edge[tot].val=val;
	edge[tot].cap=cap;
	edge[tot].flow=0;
	head[from]=tot;
}
void add_edge(int from,int to,int cap,int val){
	unuse_add_edge(from,to,cap,val);
	unuse_add_edge(to,from,0,-val);
}
int n,m;
int qu[Maxn+5],qu_f,qu_t;
int dis[Maxn+5];
bool vis[Maxn+5];
bool in_q[Maxn+5];
int S,T;
bool Dinic_bfs(){
	memset(dis,0x3f,sizeof dis);
	qu_f=1,qu_t=0;
	qu[++qu_t]=S;
	in_q[S]=1;
	dis[S]=0;
	while(qu_f!=qu_t+1){
		int u=qu[qu_f++];
		in_q[u]=0;
		qu_f%=(Maxn+1);
		for(int i=head[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(edge[i].flow==edge[i].cap){
				continue;
			}
			if(dis[u]+edge[i].val<dis[v]){
				dis[v]=dis[u]+edge[i].val;
				if(!in_q[v]){
					in_q[v]=1;
					qu_t++;
					qu_t%=(Maxn+1);
					qu[qu_t]=v;
				}
			}
		}
	}
	return dis[T]!=Inf;
}
int Dinic_dfs(int u,int flow,int &min_cost){
	if(u==T||flow==0){
		return flow;
	}
	vis[u]=1;
	int sum=0;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(dis[v]!=dis[u]+edge[i].val||vis[v]||edge[i].flow==edge[i].cap){
			continue;
		}
		int op=min(flow-sum,edge[i].cap-edge[i].flow),f;
		if((f=Dinic_dfs(v,op,min_cost))){
			edge[i].flow+=f;
			edge[((i-1)^1)+1].flow-=f;
			min_cost+=edge[i].val*f;
			sum+=f;
			if(sum==flow){
				break;
			}
		}
	}
	vis[u]=0;
	if(sum==0){
		dis[u]=Inf;
	}
	return sum;
}
int Dinic(int &min_cost){
	int ans=0;
	min_cost=0;
	while(Dinic_bfs()){
		ans+=Dinic_dfs(S,Inf,min_cost);
	}
	return ans;
}
bool _in[Maxn+5];
char _s[Maxn+5];
int _a[Maxn+5],_id[Maxn+5],_w[Maxn+5];
int *a[Maxn+5],*id[Maxn+5],*w[Maxn+5];
bool *in[Maxn+5];
char *s[Maxn+5];
int id_tot;
void init(){
	int last=0;
	for(int i=1;i<=n;i++){
		a[i]=_a+last;
		id[i]=_id+last;
		w[i]=_w+last;
		s[i]=_s+last;
		in[i]=_in+last;
		last+=m;
	}
	id_tot=tot=0;
	for(int i=1;i<=n*m+2;i++){
		_a[i]=_id[i]=_w[i]=head[i]=0;
		_in[i]=0;
		_s[i]='';
	}
}
char find_c(int nx,int ny){
	for(int i=0;i<4;i++){
		if(d[i][0]==nx&&d[i][1]==ny){
			return D[i];
		}
	}
	return '?';
}
int get_x(int id){
	return (id-1)/m+1;
}
int get_y(int id){
	return id%m==0?m:id%m;
}
void solve(){
	read(n),read(m);
	if(n==1&&m==1){
		puts("NO");
		return;
	}
	init();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			read(a[i][j]);
			id[i][j]=++id_tot;
		}
	}
	S=++id_tot;
	T=++id_tot;
	int num=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			in[i][j]=1;
			for(int k=0;k<4;k++){
				int nx=i+d[k][0],ny=j+d[k][1];
				if(nx<1||ny<1||nx>n||ny>m){
					continue;
				}
				if(a[nx][ny]<a[i][j]){
					in[i][j]=0;
					break;
				}
			}
			num+=in[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if((i+j)&1){
				continue;
			}
			for(int k=0;k<4;k++){
				int nx=i+d[k][0],ny=j+d[k][1];
				if(nx<1||ny<1||nx>n||ny>m){
					continue;
				}
				if(a[nx][ny]==a[i][j]){
					add_edge(id[i][j],id[nx][ny],1,0);
				}
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if((i+j)&1){
				add_edge(id[i][j],T,1,-(int)in[i][j]);
			}
			else{
				add_edge(S,id[i][j],1,-(int)in[i][j]);
			}
		}
	}
	int ans_2;
	Dinic(ans_2);
	ans_2=-ans_2;
	if(ans_2<num){
		puts("NO");
		return;
	}
	for(int i=head[S];i;i=edge[i].nxt){
		if(edge[i].flow==1){
			int u=edge[i].to;
			for(int j=head[u];j;j=edge[j].nxt){
				if(edge[j].flow==1){
					int v=edge[j].to;
					int u_x=get_x(u),u_y=get_y(u),v_x=get_x(v),v_y=get_y(v);
					s[u_x][u_y]=find_c(v_x-u_x,v_y-u_y);
					s[v_x][v_y]=find_c(u_x-v_x,u_y-v_y);
					w[u_x][u_y]=1;
					w[v_x][v_y]=a[u_x][u_y]-1;
				}
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(s[i][j]==''){
				for(int k=0;k<4;k++){
					int nx=i+d[k][0],ny=j+d[k][1];
					if(nx<1||ny<1||nx>n||ny>m||a[nx][ny]>=a[i][j]){
						continue;
					}
					s[i][j]=D[k];
					w[i][j]=a[i][j]-a[nx][ny];
					break;
				}
			}
		}
	}
	puts("YES");
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			printf("%d ",w[i][j]);
		}
		puts("");
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			putchar(s[i][j]);
			putchar(' ');
		}
		puts("");
	}
}
int main(){
	int T;
	read(T);
	while(T--){
		solve();
	}
	return 0;
}
原文地址:https://www.cnblogs.com/withhope/p/13751563.html