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