DFS深度优先算法

1.拿一道题举例

这道题数据量大的时候过不了,但却是很好的dfs逻辑练手题。

首先确定卡牌种类,和它的几种特性(要用到的),一共有四种1,2,12,21,其编号为1,2,3,4长度为1,1,2,2其数量为a,b,c,d,然后确定卡牌总数n 。

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	//卡牌花色一共有四种:1,2,12,21
	//卡牌的长度和数量,从下标1开始,下标0补0
	std::vector<int>length = {0,1,1,2,2 };
	std::vector<int>num(5);
	//输入单个卡牌数量,和卡牌总数量
	int n = 0;
	for (int i = 1; i <= 4; i++) {
		std::cin >> num[i];
		n += num[i];
	}
	//定义一个容器,表示步数==卡牌总数
	std::vector<int>a(n + 1);
	dfs(a,length, num, 1, n);
	std::cout << result;
	return 0;
}

2.然后按照dfs模版设置条件

模版

void dfs(参数){
    //停止条件
    

    //递归放卡牌
}

放step张卡牌的时候,我们要先查看step-1张卡牌是什么,分为四种情况。(12121212要错开)

1.a[step-1]==1(代表卡牌编号,例如12这种花色的卡牌编号为3),则a[step]只能放2或者4

2.a[step-1]==2则a[step]只能放1或者3

3.a[step-1]==3则a[step]只能放1或者3

4.a[step-1]==4则a[step]只能放2或者4

还有一种特殊的情况就是当a[step-1]==0时,及放第一张卡牌的时候(我们为什么要让下标从1开始,就是为了能够访问下标0,否则会越界)

什么是递归,无非就是套娃,这里就要用到for循环了

//这是两个全局变量,ans记录一次深度搜索到底的长度,用result表示最终最长的长度,通过比较两个
//全局变量确认最大的值
int ans, result;
void dfs(std::vector<int>&a,std::vector<int>& length, std::vector<int>& num, int step,int n) {
    //停止条件




    
	//判断条件,第一种情况,当上一张卡牌为1或者4
	if (a[step - 1] == 1|| a[step - 1] == 4) {
        //这里i的取值为2或者4,涵盖了在a[step - 1] == 1下a[step]可能得两种情况
		for (int i = 2; i <= 4; i += 2) {
			//该种卡片是否还有剩余,有剩余就继续递归
			if (num[i] != 0) {
                //放卡牌
				a[step] = i;
                //数量--
				num[i]--;
                //记录长度
				ans += length[i];
                //下一步
				dfs(a, length, num, step + 1, n);
                //收卡牌
				num[i]++;
                //长度--
				ans -= length[i];
			}
		}
	}//第二种情况
	else if (a[step - 1] == 2|| a[step - 1] == 3) {
		for (int i = 1; i <= 3; i += 2) {
			//该种卡片是否还有剩余
			if (num[i] != 0) {
				a[step] = i;
				num[i]--;
				ans += length[i];
				dfs(a, length, num, step + 1, n);
				num[i]++;
				ans -= length[i];
			}
		}
	}//特殊情况
	else {
		for (int i = 1; i <= 4; i++) {
			//该种卡片是否还有剩余
			if (num[i] != 0) {
				a[step] = i;
				num[i]--;
				ans += length[i];
				dfs(a, length, num, step + 1, n);
				num[i]++;
				ans -= length[i];
			}
		}
	}
	return;
}

下面是停止条件,我们知道,比如在a[step-1]==1或4的时候,num[2]和num[4]==0就停止,并替换最长的长度。看result和ans哪个大,大的那个作为新的result。

//1.((a[step - 1] == 1||a[step - 1] == 4) && num[2] == 0 && num[4] == 0)
//2.((a[step - 1] == 2 || a[step - 1] == 3) && num[1] == 0 && num[3] == 0)
int ans, result;
void dfs(std::vector<int>&a,std::vector<int>& length, std::vector<int>& num, int step,int n) {
    //停止条件
	if (((a[step-1]==1||a[step-1]==4) && num[2] == 0 && num[4] == 0) || ((a[step - 1] == 2 || a[step - 1] == 3) && num[1] == 0 && num[3] == 0)) {
		if (ans > result)result = ans;
	}


    //递归条件
    if……





	return;
}


上面是递归练手,下面是这道题的正解。

3.正解

1.我们知道12和21这两张牌可以自己和自己组成一个排,比如12,12,12,12,12。21,21,21但是如果12却不能和21连起来,除非有一张1或者2把它们连起来,比如12,1, 21或者21,2,12

2.我们也可以发现1和2可以组成12或者21,那么假如它们四个都不为0且1和2的数量相等,我们可以这样摆2,【12】(所有12的集合体),1,【21】,{2,1}(剩余2和1的组合体),从这个排列不难看出1和2的数量是相等的。

3.那么当a和b等于0的时候,就没有连接牌把【12】和【21】连在一起了,所以队列的最终长度是std::max(num[3],num[4])*2.

4.如果a和b不相等呢,假如1有6张,2只有4张,这样组合成{2,1}后前面最多容纳一张1,{2,1},所以,这样的情况2,【12】,1,【21】,{2,1}如果2多出来n(n>1)张,我们只能排一张在末尾,2,【12】,1,【21】,{2,1},2,剩下的都无法在排,所以从中我们可以得出,在a!=b的情况下,长度为2*(c+b)+std::min(a,b)+1;

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	int a, b, c, d;
	std::cin >> a >> b >> c >> d;
	if (a == b) {
		if (c == 0 || d == 0) {
			std::cout << a + b + 2 * (c + d);
			return 0;
		}
		if (!a && !b) {
			std::cout << 2 * std::max(c, d);
			return 0;
		}
		std::cout << a + b + 2 * (c + d);
	}
	std::cout << 2 * (c + d) + 2 * std::min(a, b) + 1;
	return 0;
}

其实递归这个东西真的很抽象,我已经尽力去解释了[捂脸],大家不妨画个图,就是高中学的排列,然后一句一句参悟一下吧。如果你能看懂,请点个赞吧。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值