基础算法·搜索·题解【约束】(全排列)


今天校内考试,差10分就AK了QAQ。
T1图论题正解BFS双端队列优化,但听说SPFA的 O ( k ∗ m ) O(k*m) O(km) O ( n ∗ m ) O(n*m) O(nm)能卡过,因为是随机数据。一念之差以为会被卡,就走上了Dijkstra的 O ( ( n + m ) l o g m ) O((n+m)logm) O((n+m)logm)的不归路……难受。
不过话说回来这次的T2也很有意思。正解是DFS但在下打挂了,写出来了但调不对,一气之下直接重构全排列暴力结果A掉,还跑得飞快。
这样就诞生了一个SAO操作。

题目

WOJ#4375 约束
WOJ题目链接

题目描述

给出 n 个互不相同的小写字母,表示出现的字符类型,以及k个约束关系:,表示 ai 必须出现在bi前面(ai,bi 不会超出所给字符类型的范围,且ai!=bi)。
请按照字典序输出所有满足约束条件的序列。
如:
n=3,字符类型为:x y z
k=1,约束条件为:x z,表示 x 必须出现在 z 的前面。
所有满足约束条件的排列有:
xyz
xzy
yxz

输入

第 1 行,2 个整数 n 和 k。
第 2 行,n 个空格隔开的字符。
接下来 k 行,每行二个字符 ai 和 bi(表示约束关系),数据保证不会出现矛盾的关系。

输出

若干行,每行 n 个字符(字符之间没有空格),表示排列的结果。

样例

  • 输入样例
    3 1
    x y z
    x z
  • 输出样例
    xyz
    xzy
    yxz

说明

对于 100%的数据:1<n<9,1≤k≤8。

题意

给出 k k k个字符对并规定其出现顺序,求合法的排列方式,并用字典序输出。

思路

首先拿到了 n n n个字符,那么既然最后要字典序输出,就先排个序吧。

然后如果要打正解的话,就该考虑一下存储方式、遍历顺序,还有最关键的,就是检查的部分。

但像我这种打不来正解的蒟蒻,就可以看一下数据规模,然后考虑全排列,然后检查每种顺序的合法性。
具体操作,可以考虑将字符存在 c [ ] c[ ] c[]里,然后再用一个 m a p &lt; c h a r , i n t &gt; map&lt;char,int&gt; map<char,int>,以便用字符查找位置。然后关于第 i i i个字符,把必须放在它前面的字符存在 t e r m [ i ] [ ] term[i][ ] term[i][]内。在检查时,把当前字符 i i i v i s vis vis设为1,遍历一遍它的 t e r m term term。如果出现没有被遍历过的 i i i前辈(误),即 v i s vis vis为0,就说明前辈一定在 i i i后面,即为非法。遇到这种情况就直接跳,否则输出答案。
至于全排列的方式……刚刚见了一个套了9个循环的同学。やれやれ,如果不是几个月前刷题时学了一个叫next_permutation的东西,可能今天就也会……一直调正解了。

代码

#include<iostream>
#include<cstdio>
#include<cctype>
#include<map>
#include<algorithm>
#include<cstring>
using namespace std;
int n,k,cnt;
int order[10]={0,1,2,3,4,5,6,7,8,9},term[10][10];
bool vis[10];
char a,b,c[10],ans[10];
map<char,int> idx;
inline int read()
{
	int x=0;bool f=0;char c=getchar();
	while(!isdigit(c)) {f|=c=='-';c=getchar();}
	while(isdigit(c)) {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f?-x:x;
}//决定了!这就是博主今后的新款读优
bool check()
{
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++)
	{
		int x=order[i];
		for(int j=1;term[x][j];j++)
			if(!vis[term[x][j]]) return 0;//当前字符的前辈必须被全部遍历过
		vis[order[i]]=1;
	}
	return 1;
}
int main()
{
	n=read(),k=read();
	for(int i=1;i<=n;i++) cin>>c[i];
	sort(c+1,c+n+1);
	for(int i=1;i<=n;i++) idx[c[i]]=i;//用字符调用位置
	for(int i=1;i<=k;i++)
	{
		cin>>a>>b;
		int m=idx[b];
		term[m][++term[m][0]]=idx[a];
	}//把位置信息存储在第0列里
	do
	{
		if(check())
		{
			for(int i=1;i<=n;i++) cout<<c[order[i]];
			cout<<"\n";
		}
	}
	while(next_permutation(order+1,order+n+1));//全排列
	return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值