网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
4.for循环步骤 :判断使用标志位i下标的数据为未被使用,条件成立->置该数组下标使用标志->置换数据a[step] = old[i],注意前者是出口移动变量,后者是i变量->递归dfs(step+1),使函数步移->回溯,置该标志位数组下标未使用标志
#define ARRAY\_LENGTH 4
#define NOT\_USED 0
#define USED 1
char old[ARRAY_LENGTH] = {'a','b','c','d'};
char a[ARRAY_LENGTH];
int useFlag[ARRAY_LENGTH];
void dfs(int step)
{
if(step >= ARRAY_LENGTH) //出口,过滤数据找出结果
{
for(int i=0;i<ARRAY_LENGTH;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
return ;
}
for(int i=0;i<ARRAY_LENGTH;i++)
{
if( useFlag[i] == NOT_USED) //判断该下标的标志位是否被使用
{
useFlag[i] = USED; //置使用标志
a[step] = old[i]; //置换数据,排列数组
dfs(step+1); //递归
useFlag[i] = NOT_USED; //回溯
}
}
}
int main(void)
{
memset(useFlag,NOT_USED,sizeof(useFlag));
dfs(0);
return 0;
}
从for循环角度理解不回溯型DFS
小明与其他3人玩牌。
一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张。
这时,小明脑子里突然冒出一个问题:
如果不考虑花色,只考虑点数,也不考虑自己得到的牌的先后顺序,自己手里能拿到的初始牌型组合一共有多少种呢?
int main()
{
int sum =0;
int total=0;
int a[14];
for( a[1]=0;a[1]<=4;a[1]++) //1
for( a[2]=0;a[2]<=4;a[2]++) //2
for( a[3]=0;a[3]<=4;a[3]++) //3
for( a[4]=0;a[4]<=4;a[4]++) //4
for( a[5]=0;a[5]<=4;a[5]++) //5
for( a[6]=0;a[6]<=4;a[6]++) //6
for( a[7]=0;a[7]<=4;a[7]++) //7
for( a[8]=0;a[8]<=4;a[8]++) //8
for( a[9]=0;a[9]<=4;a[9]++) //9
for( a[10]=0;a[10]<=4;a[10]++) //10
for( a[11]=0;a[11]<=4;a[11]++) //11
for( a[12]=0;a[12]<=4;a[12]++) //12
for( a[13]=0;a[13]<=4;a[13]++) //13
{
for(int i=1;i<=13;i++)
{
sum += a[i];
}
if(sum == 13)
{
total++;
}
sum=0;
}
cout<<total;
return 0;
}
/\*\*
parameter: n:步移 ; cartNum:手上的牌数量 ; 这两个变量为控制出口变量。
\*/
void dfs(int n,int cartNum)
{
if(n>14) //出口1:抽牌的次数(13次)越界 相当于for循环阶数:13阶
{
return;
}
if(cartNum>=13) //出口2:牌数量越界
{
if(cartNum==13) //截取条件成立的结果
sum++;
return;
}
else //相当于for循环中的条件int a=0~a<=4;
{
dfs(n+1,cartNum); //没有该类的牌
dfs(n+1,cartNum+1); //有一张该类的牌
dfs(n+1,cartNum+2); //有两张该类的牌
dfs(n+1,cartNum+3); //有三张该类的牌
dfs(n+1,cartNum+4); //有四张该类的牌
}
}
竞赛应用
带分数
100 可以表示为带分数的形式:100 = 3 + 69258 / 714
还可以表示为:100 = 82 + 3546 / 197
注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。
类似这样的带分数,100 有 11 种表示法。
题目要求:
从标准输入读入一个正整数N (N<1000*1000)
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!
例如:
用户输入:
100
程序输出:
11
再例如:
用户输入:
105
程序输出:
6
资源约定:
峰值内存消耗 < 64M
CPU消耗 < 3000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型。
解法一
dfs N=A+B/C⇨B=(N-A)*C。根据公式,只需要求A和C就可以进行判断了。A的范围是1N,但是这些数中有很多是无用的(如:11,101……)。我们只选有用的数,用vis数组标记19中已经用过的数字,避免做不必要的搜索。
#include <iostream>
using namespace std;
#include<algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
int ans = 0; //
bool vis[10];
int n; int A = 0, C = 0;
int lenN; int ALEN = 0, CLEN = 0;
bool judge() {
bool vistemp[10];
memcpy(vistemp, vis, sizeof(vis));
int t = (n - A)*C; int BLEN = 0;
while (t) {
if (vistemp[t % 10]) return false;
vistemp[t % 10] = 1;
BLEN++;
t /= 10;
}
return BLEN+ALEN+CLEN==9;
}
void dfs(int flag)
{
if (flag == 2)
{
if (judge()) ans++;
return;
}
for (int i = 1; i <= 9; i++)
{
if (vis[i]) continue;
vis[i] = 1;
if (flag == 1)
{
A *= 10; A += i; ALEN += 1;
if (A<=n)
{
dfs(1);
dfs(3);
ALEN -= 1; A /= 10;
}
else if (A > n)
{
ALEN -= 1; A /= 10;
vis[i] = 0;
break;
}
}
else if (flag == 3) {
if (CLEN < (9 - ALEN) / 2)
{
C *= 10; C += i; CLEN++;
dfs(2);
dfs(3);
C /= 10; CLEN--;
}
else { vis[i] = 0; break; }
}
vis[i] = 0;
}
}
int main() {
cin >> n;
int temp = n;
lenN = 0;
while (temp) { lenN++; temp /= 10; }
memset(vis, 0, sizeof(vis));
vis[0] = 1;
dfs(1);
cout << ans << endl;
return 0;
}
地宫取宝
X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。
地宫的入口在左上角,出口在右下角。
小明被带到地宫的入口,国王要求他只能向右或向下行走。
走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。
当小明走到出口时,如果他手中的宝贝恰好是k件,则这些宝贝就可以送给小明。
请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这k件宝贝。
【数据格式】
输入一行3个整数,用空格分开:n m k (1<=n,m<=50, 1<=k<=12)
接下来有 n 行数据,每行有 m 个整数 Ci (0<=Ci<=12)代表这个格子上的宝物的价值
要求输出一个整数,表示正好取k个宝贝的行动方案数。
该数字可能很大,输出它对 1000000007 取模的结果。
例如,输入:
2 2 2
1 2
2 1
程序应该输出:
2
再例如,输入:
2 3 2
1 2 3
2 1 5
程序应该输出:
14
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型。
解法一
4维的记忆化DFS,题目要求只能下或右走,从左上角走到右下角,记录下每个坐标下的数量和最大宝贝价值下的走的情况
#include<stdio.h>
#include<string.h>
#include<string>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<time.h>
#define mod 1000000007
using namespace std;
int n,m,k;
int c[55][55];
int dp[55][55][101][15];
int dfs(int x,int y,int mun,int v) //x,y表示坐标,mun表示手中宝贝的数量,V表示手中的最大宝贝的价值
{
if(dp[x][y][mun][v]!=-1)//记录了经过 此坐标此数量此价值下的所有走法,
return dp[x][y][mun][v];//所以直接返回,后面的无需走了
if(x==n-1&&y==m-1)
{
if(mun==k||(mun==k-1&&c[x][y]>v))//符合要求的算一种
return dp[x][y][mun][v]=1;
else
{
return dp[x][y][mun][v]=0;//不符合
}
}
int t=0;
if(y+1<m))//x<n-1
{
if(c[x][y]>v)//选择拿起宝贝
{
t=(t+dfs(x,y+1,mun+1,c[x][y]))%mod;
}
t=(t+dfs(x,y+1,mun,v))%mod;//选择不拿
}
if(x+1<n)//x<n-1
{
if(c[x][y]>v)
{
t=(t+dfs(x+1,y,mun+1,c[x][y]))%mod;
}
t=(t+dfs(x+1,y,mun,v))%mod;
}
return dp[x][y][mun][v]=t%mod;//由右和下返回的值记录于此坐标下,下次路过此时
//不再递归
}
int main()
{
memset(dp,-1,sizeof(dp)); //标志
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
scanf("%d",&c[i][j]);
c[i][j]++;//为什么加1,因为有的宝贝价值问0,会出现比较错误
}
printf("%d\n",dfs(0,0,0,0));
return 0;
}
贪心
动态规划
常用函数
C常用头文件
标准输入输出
#include < cstdio >
scanf()
printf()
标准库
#include < cstdlib >
qsort()
数学库
#include < cmath>
字符串库
#include < cstring >
memset(array_first_address, NUM, sizeof(array));
C常用头文件
标准输入输出
#include < iostream >
using namespace std;
cin>>
cout<<
算法库
#include < algorithm >
next_permutation(array_first_address , array_first_address+length);
sort(array_first_address , array_first_address+length);
其他技巧
康托展开式公式:
X=an*(n-1)!+an-1*(n-2)!+…+ai*(i-1)!+…+a2*1!+a1*0!
这个式子是由1到n这n个数组成的全排列,共n!个,按每个全排列组成的数从小到大进行排列,并对每个序列进行编号(从0开始),并记为X。
比如说1到4组成的全排列中,长度为4,1234对应编号0,1243对应编号1
那a1,a2,a3,a4是什么意思呢?==>>
对1到4的全排列中,我们来考察3214,则
a4={3在集合(3,2,1,4)中是第几大的元素}=2
a3={2在集合(2,1,4)中是第几大的元素}=1
a2={1在集合(1,4)中是第几大的元素}=0
a1=0(最后只剩下一项)
则X=2*3!+1*2!+0*1!+0*0!=14,即3214对应的编号为14。
因此,此题用康托展开式就可以出来了,将abcd…写成1,2,3,4…就行了。
long long a[ 17 ];
long long fun(long long n) //求阶乘
{
long long s = 1;
for(int i = 1;i<=n;i++)
s*=i;
return s;
}
int main()
{
long long t;
int f[] = {2,3,11,6,17,12,1,10,8,5,13,7,9,15,4,14,16};
long long sum = 0;
a[0] = 1;
for(int i=1;i<17;i++)
{
a[i] = a[i-1]*i;
}
for(int i = 0;i<16;i++)
{
t = 0;
for(int j = i+1;j<17;j++)
{
if(f[j]<f[i])
![img](https://img-blog.csdnimg.cn/img_convert/042d440b5a82a7a3c37d4beb9208062a.png)
![img](https://img-blog.csdnimg.cn/img_convert/e9521b933ed0056e0800978b75ea31e2.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
,15,4,14,16};
long long sum = 0;
a[0] = 1;
for(int i=1;i<17;i++)
{
a[i] = a[i-1]*i;
}
for(int i = 0;i<16;i++)
{
t = 0;
for(int j = i+1;j<17;j++)
{
if(f[j]<f[i])
[外链图片转存中...(img-Y2w1iX7S-1715887818797)]
[外链图片转存中...(img-osIV5mbH-1715887818797)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**