DFS 排列与组合的简单搜索
链接:Safecracker<HDU 1015>
题意:
一个char 的映射:(A=1, B=2, …, Z=26).
从一个给定的string里选取五个字符,给定target,求
v - w^2^ + x^3^ - y^4^ + z^5^ = target
要求vwxyz 字典序最大
题目先摆着里,可以思考一下!这里讲两种简单的DFS搜索
DFS 与 组合
这类题目是,求n个数(n<=26),选取k个数,这k个数满足一个规则,但是这k个数顺序可以互换(比如这k个数的和或积等规则)
例题:上次我的博客第四题 烤鸡
猪猪 Hanke 特别喜欢吃烤鸡(本是同畜牲,相煎何太急!)Hanke 吃鸡很特别,为什么特别呢?因为他有 10种配料(芥末、孜然等),每种配料可以放 1 到 3 克,任意烤鸡的美味程度为所有配料质量之和。
现在, Hanke 想要知道,如果给你一个美味程度 n ,请输出这 10 种配料的所有搭配方案。
输入
11
输出
10
1 1 1 1 1 1 1 1 1 2
1 1 1 1 1 1 1 1 2 1
1 1 1 1 1 1 1 2 1 1
1 1 1 1 1 1 2 1 1 1
1 1 1 1 1 2 1 1 1 1
1 1 1 1 2 1 1 1 1 1
1 1 1 2 1 1 1 1 1 1
1 1 2 1 1 1 1 1 1 1
1 2 1 1 1 1 1 1 1 1
2 1 1 1 1 1 1 1 1 1
思路:每个数都有选与不选两个状态,或者选1选2或选几的状态,
状压枚举是之后写的,在这里:
C++ 枚举问题入门.
不讲状压,我们使用DFS实现的话
ADT:
void dfs(int position,int now,int end){
if(position == end){ //search end
if(now == targer){ //get your target
//dosomething, ex. print
}
return ;
}
dfs(positon+1 ,now ,end); //we dont choose
dfs(positon+1 ,now+arr[position] ,end); //we do choose
}
烤鸡题目的代码:
void pd(int pos,int now){
if(pos==10){ //边界1
if(now!=n)return;
ans++;
for(int i=1;i<=10;++i){
aa[ans][i]=cas[i]; //第ans种情况,先存起来
}
return ;
}
if(now>n)return ; //边界3
if(10-pos + now > n || (10-pos)*3 + now <n)return ; //边界2
cas[pos+1]=1; //这位放1 递归,下同理
pd(pos+1,now+1);
cas[pos+1]=2;
pd(pos+1,now+2);
cas[pos+1]=3;
pd(pos+1,now+3);
return ;
}
是不是有异曲同工的感觉?
DFS 与 排列
回到HDU该题,这题目不光光是选取哪些结点,还要考虑到每个结点的排列,字母的不同位置对答案有不同的影响。
那就需要一个数组记住我们该位置选了哪个数,
用另一个数组记录我们目前是否选取了这个数。
ADT:
int arr[MAX]; //input array
int ans[MAX]; //answer array
bool vis[MAX]; //to mark if we have already chosen it
void dfs(int position,int end){
if(position == end){ //search end
if(now == targer){ //get your target
//dosomething, ex. print ans array
}
return ;
}
for(int i=0;i<end;++i){ //search all the nums
if(!vis[arr[i]]){ //if we haven't chosen it
vis[arr[i]] = true; //we choose it
ans[position] = arr[i]; //mark the num we choose at this position
dfs(position+1,end); //we dfs
vis[arr[i]] = false; //search finish ,then we choose to don't choose it ,which means back-track ,回溯
}
}
}
回到本题代码,我们需要排列而非单单组合,因此需要使用第二种dfs搜索
仔细考虑一下需要字典序最大,那么我们把字符降序排列就ok了
再考虑一下,一般我们搜索到的第一个答案就是最终答案了,那么之后我们都不需要继续搜索了,需要一个简单的剪枝。
AC代码:
#include <bits/stdc++.h>
#define show(x) std::cerr << #x << "=" << x << std::endl;
#define IOS ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
#define INF 1e5
using namespace std;
typedef long long ll;
const int MAX = 101;
char aa[MAX];
bool vis[MAX];
char ans[MAX];
string STR;
int n;
bool fd;
ll x;
void dfs(int p,ll shu){
if(fd)return ; //prune
if(p==5){
if(shu==x){ //which means we find it
fd=1;
for(int i=0;i<5;++i){ //input
cout << ans[i];
}
cout << endl;
}
return ;
}
for(int i=0;i<n;++i){
if(fd)return ; //prune
if(!vis[aa[i]-'A']){
vis[aa[i]-'A']=1;
ans[p] = aa[i];
dfs(p+1,shu+pow(aa[i]-'A'+1,p+1)*pow(-1,p));//calculation
if(fd)return ; //prune
vis[aa[i]-'A']=0;
}
}
}
int main()
{
///IOS;
while(cin >> x >> STR){
if(x==0 && STR == "END")break;
n = STR.size();
fd = 0;
memset(vis,0,sizeof(vis)); //dont forget to memset the vis array
for(int i=0;i<n;++i){
aa[i] = STR[i];
}
sort(aa,aa+n,greater<char>()); //desc
dfs(0,0);
if(!fd)cout << "no solution" << endl;
}
return 0;
}
/**
1 ABCDEFGHIJKL
11700519 ZAYEXIWOVU
3072997 SOUGHT
1234567 THEQUICKFROG
0 END
*/
之前写的时候一开始使用了组合 dfs,后来想起来应该用排列 dfs,于是写了这片blog,谨记我当时的傻hhh