usaco--frame

题目给出一个方块互相叠加的图,求出所有可能的重叠顺序

解题思路:按照NOCOW的题解建好图之后,就是求一个拓扑排序的所有解了,搜索的时候按照字典序搜索就可以按照字典序求出所有的答案,这个题唯一的难点就是输出拓扑图所有的解,以前按照删边求的方法只能求一种解,脑筋不知道怎么就不灵光了,翻了资料才知道怎么求,其实也简单的。PS:表示对USACO好无语,数组越界报超时,好无辜的debug好久

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<fstream>
#define zout cout
#define maxn 140
#define M(a) memset((a),0,sizeof(a))
using namespace std;
ifstream fin("frameup.in");
ofstream fout("frameup.out");
struct Ch
{
	int mix,miy,mx,my;
	Ch()
	{
		mx=my=-100;
		mix=miy=100;
	}
};      //储存每个矩形的最小坐标和最大坐标
Ch cor[100];
int n,m;
int g[maxn][maxn]; 		//图的邻接矩阵存储
char f[maxn][maxn];		//输入的字符矩阵
int apl[maxn],a[maxn],result[maxn*100][maxn];
bool vis[maxn];
int tot,ir;
bool init()
{
	if(fin>>n>>m)
	{
		ir=0;
		M(g);M(apl);
		tot=0;
		for(int i=0;i<n;i++)
		fin>>f[i];
		return true;
	}
	return false;
}
void upd(int u,int x,int y)		//更新每个矩阵的临界坐标函数
{
	Ch & t=cor[u];
	if(x>t.mx)t.mx=x;
	if(y>t.my)t.my=y;
	if(x<t.mix)t.mix=x;
	if(y<t.miy)t.miy=y;
}
void build_graph()		//建图过程,参考nocow
{
	for(int i=0;i<26;i++)
	{
		Ch & t = cor[i];
		if(apl[i])
		{
			int v,u = i;
			for(int x=t.mix;x<=t.mx;x++)
			{
				if(f[x][t.miy]!=i+'A')
				{
					v=f[x][t.miy]-'A';
					g[u][v]=1;
				}
				if(f[x][t.my]!=i+'A')
				{
					v=f[x][t.my]-'A';
					g[u][v]=1;
				}
			}
			for(int y=t.miy;y<=t.my;y++)
			{
				if(f[t.mx][y]!=i+'A')
				{
					int v=f[t.mx][y]-'A';
					g[u][v]=1;
				}
				if(f[t.mix][y]!=i+'A')
				{
					int v=f[t.mix][y]-'A';
					g[u][v]=1;
				}
			}
		}
	}
}
int isStart(int st)   //判断一个点St的入度是否为0,是返回true,不是返回false
{
	bool ff[maxn];
	M(ff);
	int e=0;
	for(int i=0;i<26;i++)
	{
		if(apl[i])
		{
			e=0;
			for(int j=0;j<26;j++)
			if(g[i][j])
			{
				if(j==st)return false;
			}
		}
	}
	return true;
}
void  op()
{
	char ch;
	for(int i=0;i<ir;i++)
	{
		for(int j=0;j<tot;j++)
		{
			ch='A'+result[i][j];
			zout<<ch;
		}
		zout<<endl;
	}
}
void dfs(int u,int a[],int dep)  //改进后的dfs求拓扑排序,数组a记录了深搜树到达叶子节点的路径,也就是答案
{
	if(dep==tot)
	{
		for(int i=0;i<tot;i++)
		result[ir][i]=a[i];
		ir++;
	}
	int f=0;
	for(int i=0;i<26;i++)
	{
		if(apl[i]&&isStart(i))
		{
			f=1;
			apl[i]=0;
			a[dep]=i;
			dfs(i,a,dep+1);
			apl[i]=1;
		}
	}
	if(f)return;
	for(int i=0;i<26;i++)
	{
		if((apl[i]&&g[u][i]))
		{
			apl[i]=0;
			a[dep]=i;
			dfs(i,a,dep+1);
			apl[i]=1;
		}
	}
}
void slove()
{
	for(int i=0;i<n;i++)
	for(int j=0;j<m;j++)
		if(f[i][j]!='.')
			apl[f[i][j]-'A']=1;
	tot=0;
	for(int i=0;i<26;i++)
	if(apl[i])tot++;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		if(f[i][j]!='.')
		{
			upd(f[i][j]-'A',i,j);
		}
	}
	build_graph();
	for(int i=0;i<26;i++)
	{
		if(apl[i]&&isStart(i))
		{
			apl[i]=0;
			a[0]=i;
			dfs(i,a,1);
			apl[i]=1;
		}
	}
	op();
}
int main()
{
	init();
	slove();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值