天天看点

【DFS 排列与组合的简单搜索】Safecracker [HDU 1015]DFS 排列与组合的简单搜索

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

继续阅读