洛谷Luogu-2741 [USACO4.4]重叠的图像Frame Up题解(详) HQG_AC的博客

https://www.luogu.org/problemnew/show/P2741

这题整体难度不大,但有很多细的东西,所以容易错。
1.读入矩阵
2.求出有多少个图像(即矩阵中有多少种不同的字母),并从小到大排序
3.t表示有几张图像,从1~t枚举出压在第i张图像上面的图像(x),构成拓扑结构,即i是x的入度,必须有i才有x
4.做一遍dfs,搜出所有的可能:
   ①枚举当前没有入度的节点(代码中是f[i]==0),将它加入答案,标记已使用
   ②将以i为入度的点的f减一,

   ③重复①,②

还有,如果在洛谷提交的话,请不要开O2优化,会莫名其妙的WA一个点的。


代码上有注释,易懂:

#include <bits/stdc++.h>
#define INF 100
using namespace std ;
char a[31][31],x[31] ;//x代表 第i号是字母c 
int y['Z'] ; // 字母c是第i号 
int t=0 ;//有几个不同的字母 
int f[31] ;//图像i下面压着多少个图像 
int sum[31] ;//表示i上面压着几张图片。拓扑排序后面好删 
int b[31][31] ;//表示i上面压的第j张图像是x 
char ans[100] ;//一个答案串 
int vis[31] ;//当前字母是否在搜索中已经出现过 
int h[31][31] ;//表示i上面是否已经搜索到j在i上面 
int n,m ;
int hash['Z'] ;
void dfs(int k,int val)
{
	if (k==t) //已经枚举过了t个图像 
	{
		for (int i=1;i<=t;i++) printf("%c",ans[i]) ;
		printf("\n") ;
		return ; 
	}
	for (int i=1;i<=t;i++)
	{
		if (f[i]==0 && !vis[i]) //入度为0 且没有访问过 
		{
			vis[i]=1 ;
			ans[k+1]=x[i] ;
			for (int j=1;j<=sum[i];j++)
			f[b[i][j]]-- ;
			dfs(k+1,i) ;//dfs(i,j)表示刚刚定下第j个位置放i 
			for (int j=1;j<=sum[i];j++)
			f[b[i][j]]++ ;
			vis[i]=0 ;
		}
	}
	//题目已保证读入数据合法,不用担心没有答案 
	return ;
}
int main()
{
	scanf("%d%d",&n,&m) ;
	for(int i=0;i<n;i++)  //读入矩阵 
    {  
        scanf("\n");  
        for(int j=0;j<m;j++)  
        {  
        	scanf("%c",&a[i][j]);  
        }
    } 
	for (int i=0;i<n;i++) 
	for (int j=0;j<m;j++){
		char c=a[i][j] ; 
		if (!hash[c] && isalpha(c)){ //当前字母新出现  
			x[++t]=c ;//x 第i号是字母c 
			hash[c]++ ;
		} 
	}
	for (int i=1;i<=t;i++) //排成字典序 
	for (int j=i+1;j<=t;j++)
	if (x[i]>x[j]){
		char tmp=x[i];x[i]=x[j];x[j]=tmp ;
	}
	for (int i=1;i<=t;i++) //字母c是第i号 
	{
		y[x[i]]=i ;
	}
	for (int k=1;k<=t;k++) //枚举t张图 
	{
		int minx=INF,maxx=-INF,miny=INF,maxy=-INF ;
		for (int i=0;i<n;i++) //图像的位置,只需要知道minx,maxx,miny,maxy 
		for (int j=0;j<m;j++){
			if (a[i][j]==x[k]){
				minx=min(minx,i);miny=min(miny,j);
				maxx=max(maxx,i);maxy=max(maxy,j);
			}
		}
		int i=maxx ;
		for (int j=miny+1;j<=maxy-1;j++){
			if (a[i][j]!=x[k] && !h[k][y[a[i][j]]]){ //B向A连边
				b[k][++sum[k]]=y[a[i][j]];//拓扑排序,表示图像b(a[i][j])的下面有图像a(x[k])  
				f[y[a[i][j]]]++; //图像b的前+1	
				h[k][y[a[i][j]]]=1 ;//判重 
			} 
		}
		i=minx ;
		for (int j=miny+1;j<=maxy-1;j++){
			if (a[i][j]!=x[k] && !h[k][y[a[i][j]]]){
				b[k][++sum[k]]=y[a[i][j]];
				h[k][y[a[i][j]]]=1 ;
				f[y[a[i][j]]]++;
			}
		} 
		int j=maxy ;
		for (int i=minx;i<=maxx;i++){
			if (a[i][j]!=x[k] && !h[k][y[a[i][j]]]){
				b[k][++sum[k]]=y[a[i][j]];
				f[y[a[i][j]]]++;
				h[k][y[a[i][j]]]=1 ;
			}
		}
		j=miny ;
		for (int i=minx;i<=maxx;i++){
			if (a[i][j]!=x[k] && !h[k][y[a[i][j]]]){
				b[k][++sum[k]]=y[a[i][j]];
				f[y[a[i][j]]]++;
				h[k][y[a[i][j]]]=1 ;
			}
		}
	}
	for (int i=1;i<=t;i++) 
	if (f[i]==0) //入度为0的点 
	{
		vis[i]=1;
		ans[1]=x[i] ;
		for (int j=1;j<=sum[i];j++)
		f[b[i][j]]-- ;
		dfs(1,i) ;//dfs(i,j)表示刚刚定下第j个位置放i 
		for (int j=1;j<=sum[i];j++) //清标记 
		f[b[i][j]]++ ;
		vis[i]=0 ;
	} 
	 return 0 ;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值