① 将一个十进制的数转化成base进制的数(想原理)
while(val) {
t[count++] = val % base; //val中有多少个base。(因为形式中每个数必须<进制数base,所以取余)
val /= base; //循环n次,则此行得到val中有多少个base的n次方。
//显然,这里得到的t数组表示的形式是反过来的。
}
②将一个base进制的数转化成十进制值(想原理)
ans = 0;
weight = 1;
while(val) {
r = val % 10; //先取出各个位上的数
val /= 10;
if(r >= base) //如果某位上的数大于其进制数了,肯定是不合理的。
return -1;
ans += weight * r; //weight表示每个位上的一个权值
weight *= base;
}
return ans;
③
异或:a^x=v可推出a=v^x和x=a^v
④
异或:
因为a^a=0,那么要找出单独的数(唯一一个出现奇数次的数),只需要将所有的数进行异或运算即可。
要求异或和,初始值sum设为0
⑤
Ctrl+H记事本替换内容。
⑥
多位小数输出用printf("%lf",a);
⑦
回文串的查找(动态规划):dp[i][j](bool型)表示i~j是不是一个回文串,循环外->内依次是:len长度,起点i,终点j,转移方程是若dp[i+1][j-1]==true,dp[i][j]=true。注意之前要先初始化好dp[i][i]和连续相同的dp[i][i+1]为true。
最后的string的截取子串函数为substr(起点,子串长度)
string longestPalindrome(string s) {
int n = s.length();
int longestBegin = 0;
int maxLen = 1;
bool table[1000][1000] = {false};
for (int i = 0; i < n; i++) {
table[i][i] = true;
}
for (int i = 0; i < n-1; i++) {
if (s[i] == s[i+1]) {
table[i][i+1] = true;
longestBegin = i;
maxLen = 2;
}
}
for (int len = 3; len <= n; len++) {
for (int i = 0; i < n-len+1; i++) {
int j = i+len-1;
if (s[i] == s[j] && table[i+1][j-1]) {
table[i][j] = true;
longestBegin = i;
maxLen = len;
}
}
}
return s.substr(longestBegin, maxLen);
}
⑧
注意动态规划中dp数组的定义 从而正确写出状态转移方程。比如说问机器人的路线这道题:
dp[i][j]=dp[i-1][j]+dp[i][j-1];
类似于爬楼梯的方案数:
int climbStairs(int n) {
int dp[n+1];
for(int i=0;i<=n;i++)
{
if(i==0)
{
dp[i]=0;continue;
}
if(i==1)
{
dp[i]=1;continue;
}
if(i==2)
{
dp[i]=2;continue;
}
dp[i]=dp[i-1]+dp[i-2]; //到了第i步有多少种不同走法总数取决于跨一步前的走法数+跨两步前的走法数
}
return dp[n];
“方案数”的这种动态规划,我再强调一次,记住了,要注意初始值(临界值)!!!!!
⑨
求最值的dp(如果是在连续的段上操作)则不一定要用max/min函数,而是用“拖累思想”(我前面的dp有没有拖累当前的元素):
(1)求最大子段和
for(int j=2; j<=n; j++) {
cin >> now;
if(0 > sum)
// 不单调递增时(之前子段和为负)==》“拖累”了当前元素,把当前的元素预存为另外的一个子段
sum = now, sumstart = j;
else
// 单调递增
sum += now;
// 当前正在进行计算的最大子段和超过之前的最大子段和,则重置最大子段和
if(sum > max)
max = sum, maxstart = sumstart, maxend = j;
}
(2)求最大子矩阵和
起点行i,终点行i,起点列k。s[k]是“竖条和”。
for(i=1;i<=n;i++) //起点行
{
memset(s,0,sizeof(int)*510);
for(j=i;j<=n;j++) //终点行
{
for(k=1;k<=m;k++) //列上的遍历
{
s[k] += a[j][k];
if(dp[k-1] > 0) //状态转移——“拖累思想”。若dp[k-1]>0则不会拖累当前数。
{
dp[k] = dp[k-1]+s[k];
}else{
dp[k] = s[k];
}
if(dp[k]>max)
{
max = dp[k];
}
}
}
}
我突然在想,这种连续的即每个元素都要放入的(和背包问题的“选放”区别)的dp状态转移方程,感觉一般是和dp[i-1]或者dp[i-2]什么的就近找关系,不用拉很远。
10.
八皇后问题+N皇后问题
两个函数:place(int q),attack(int q, int i)。主要是前两个函数,place要递归(注意一个皇后可能有多个不同摆放位置-->多方案),皇后序号即其所放置的行号。place是给你找列号,attack是看你用这个列号的话会不会和之前摆好的冲突,如果能摆好的话place递归去摆下一个,或者进入下次循环给当前重新摆个位置。
bool attack(int p,int qq)
{
if(p==1)
return false;</span>
else
{
for(int i=1;i<p;i++)
{
if(q[i]==qq)
return true;
if(abs(q[i]-qq)==abs(i-p))
return true;
}
return false;
}
}
void place(int p)
{
if(p==9)
{
total_solution+=1; return;
}
for(int i=1;i<=N;i++) //对第p行的列上递归 //N是皇后数量,也是行、列数
{
if(attack(p,i)==false)
{
q[p]=i;
place(p+1);
}
}
}
11.
*+表达式求值:
a*b+c
a存在temp里,遇到*,tempx*=temp,b存在temp里,遇到+则让tempx*=temp再加到ans里,c存在temp里,到末尾了,如果tempx为1(即之前都被加到ans里了),则ans+=temp,否则则知道最后一个符号其实是*,应该让tempx*=temp再加到ans里。
就是一种顺序处理的逻辑设计,举个例子想想多种情况,考虑全面一点,进行完善的程序设计。
12.
【二进制枚举】
比如说有14次,每次可能有两种情况,就能用到二进制枚举。
李白喝酒:
int main(){
int ans=0;//方案数
for (int i=0; i<(1<<14); i++) {
int dian=0;//表示遇到店的次数
int hua=0;//表示遇到话的次数
int num=2;//初始酒壶有两斗
for (int j=0;j<14 ;j++) {
if (i&(1<<j)) {//这里判断二进制i从右数第j+1为是否为1 //注意这里写法哟 不能==1什么的
dian++;//遇到店,次数加1
num*=2;//加一倍
}else{
hua++;//遇到花,次数加1
num-=1;//喝一斗
}
}
if (dian==5&&hua==9&&num==1) {
++ans;//记录方法数
}
}
printf("%d\n",ans);
13.
蔡算星期几公式:
w=(d+2*m+3*(m+1)/5+y+y/4-y/100+y/400)%7
结果再+1则为真正的星期几啦~
注意每年的1、2月要当做上一年的13、14月来算(即y-1 m取13、14)
14.
字符小写转大写:-=32
15.
16.
【map】
直接dict["Tom"]=1相当于插入效果。
可以count看看存不存在。
遍历:map<string,int> iterator:: it=dict.begin();it!=dict.end();it++
17.
map排序:
map本身就是按键值对的first(key-value)在插入的时候就自动排序了的,但如果想让map从大到小排序或者按自定义的顺序排序,显然需要用到sort函数结合cmp函数。但是map是不能去sort的,sort只能针对线性。
解决方法就是定义一个vector来存map的东西:vector<pair<string,int> >vec(dict.begin(),dict.end()),然后对vector来sort。
bool cmp(pair<int,int>&a,pair<int,int>&b) //①自定义计算机的"小于"
{
if(a.second>b.second)
return true;
else if(a.second==b.second && a.first>b.first)
return true;
else
return false; //这个else语句一定不能漏掉
}
int main()
{
map<int,int> m;
int n;
cin>>n;
while(n--)
{
int a;
cin>>a;
if(m.count(a)==0) m[a]=0; //②
m[a]++;
}
vector<pair<int,int>>vec(m.begin(),m.end()); //③
sort(vec.begin(),vec.end(),cmp);
cout<<vec.begin()->first<<" "<<vec.begin()->second;
return 0;
}
18.
【队列】
敲7:
n个人,第m个人开始报报的t,数里有7或是7的倍数的出局,求最后剩下的一个人。
利用“队列前出后插的定义”,想象把这个圆桌拉成一条线(一个队伍),遵守先进先出的原则。如果数里有7则直接pop,否则的话就乖乖从队头出来,插入到队伍后面去(等着下次轮到你报数)。
任务系统:
蒜头君设计了一个任务系统。这个系统是为了定时提醒蒜头君去完成一些事情。
系统大致如下,初始的时候,蒜头君可能会注册很多任务,每一个任务的注册如下:
Register Q_num Period
表示从系统启动开始,每过 PeriodPeriod 秒提醒蒜头君完成编号为 Q_{num}Qnum 的任务。
你能计算出蒜头君最先被提醒的 kk 个任务吗?
发现没有?跟敲7为啥都用队列呢?因为其实都可以理解成“一个事物这次来了之后,下次还会来,就跟在排队一样,排到了又重新去排队等待”!!!!!
这里用优先队列,自动让队列排序。但是!这个要注意了,是我自定义的结构体作为了优先队列的元素,而优先队列的排序将会遵守我结构体里定义的“bool operator <(const node &b) const”函数的定义。(男左女右,应该没得问题的)
while(k--)
{
printf("%d\n",q.top().num);
s1=q.top();
s1.times+=1;
s1.period=s1.times*s1.onetime_period;
q.pop();
q.push(s1); //再去排队(此处为优先队列则自动排序~)
}
19.【优先队列】【n个最小和】
思想:我跟你现在是最小的了,那么说明我牛逼,我跟你后面那个可能也会是下一次的最小的~~
#include<iostream>
#include<queue>
#include<bits/stdc++.h>
using namespace std;
const int mmax=50010;
int A[mmax],B[mmax];
struct Item
{
int s,b;
Item(int s,int b):s(s),b(b){}
bool operator < (const Item &b) const
{
return s>b.s;
}
}
priority_queue<Item> q;
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>A[i];
}
for(int i=0;i<n;i++)
{
cin>>B[i];
}
sort(A,A+n);
sort(B,B+n);
for(int i=0;i<n;i++)
{
q.push(Item(A[i]+B[0],0));
}
for(int i=0;i<n;i++)
{
Item item=q.top();
q.pop();
cout<<item.s;
if(i!=n-1)
cout<<" ";
int b=item.b;
if(b+1<n)
q.push(Item(item.s-B[b]+B[b+1],b+1));
}
return 0;
}
20.
走迷宫路数 dfs(x,y,n,m,a[][]) :x y是当前到达的点的坐标,n m是图形的行和列数,a数组是输入的图形
dfs临界条件是达到终点‘T’,使用xx和yy数组进行移动,判断出界或者访问过则continue,在移动之后该点的vis要归回=0. (理解记忆:因为可以想象这个格子是终点的前一个格子,且唯一,那么不能只访问一次就不能访问了吧! )
21.
油田问题。
省赛有道类似题。去dfs周围的,把它那一坨的都变成其他符号,方便我主函数里的计数。
22.
方程的解数。dfs(i,sum) :i是当前该算xi了,sum是当前已得到的和。
这道题让我觉得dfs的题有一个“题型思路上的共同点”:多个点,如果每个点有多种选择(状态),则可以通过dfs来进行“选择”,以此进行递归,注意把临界条件找到。
但这个时候如果能用dfs,很有可能可以用动态规划——尤其是问你“方案数”“路线数”的(那么dp的含义应该就是所求)!!!。
23.
蒜头君找钥匙回家。S起点,P钥匙,T家。
两次bfs最短路径,S出发找P 和 T出发找P,加和枚举能到达的P的两值之和取最小。就知道要取哪个钥匙能最短路径了。
24.
动态规划的思考!先把dp含义想好之后,感觉可以考虑考虑i-1 或者 i-2 (减得很少或者减得是特殊数据)的 dp ,思考状态转移方程。
25.
传娃娃游戏。只能左右传一人,问从A开始传传m次回到他手里,有多少种传法。
刚开始觉得,诶?多个点?每个点有两种选择。。dfs??要明白,dfs可能就是可以用动态规划来做的!!!况且这里是求“路线数”“方案数”,多半动态规划了!dp数组含义就是“方案数”!这时候想dp的下标然后想转移方程。咦?如果觉得下标选取一下没思路,就把题目中的变量都列出来,挨个组合看看弄一维还是二维啥的!这里dp[i][j] i为传到了第i次,j为同事序号,这样状态方程及其好些!
26.
strstr(s,subs) 可以用来查找子串(返回值为子串index,没有则返回NULL)!
可以用strstr来“看看子串存不存在”,而一看就是那种复杂度会比较大的字符串匹配题肯定还是用算法。
27.
卡特兰数:先把n个以1~n编号看作第一组,另外n个看作第二组,第二组中的每一个可以选择去匹配第一组中的任意一个。所有方案总数即h[n]。
这个理解肯定没问题,再看看应用:https://blog.csdn.net/hackbuteer1/article/details/7450250
28.
动态规划——两条不相交的走迷宫。简化题意后发现就是走两条迷宫里的路 不相交即可,dp[i][j][k][d]。转移方程是2*2=4种。
29.
多重背包来做。dp[half]==half即可。
30.
https://blog.csdn.net/m0_38033475/article/details/79497154
【状态压缩dp】【子集i、补集i^t】
求移除字符串的最小操作数,回文串可以一次性移除。
其实状压dp就是把“下标”变为“二进制数”以表示“选取情况”“组合方案”!
dp[t]表示选取情况为t时的最小操作次数。
这里假设t为选取情况,若t是回文串,那么dp[t]=1,否则=inf,需要去看选取t的子集和补集之和(dp[i]+dp[i^t])取最小值,从而知道dp[t]的最小值。
船运载小车。(这道题好好看理解一下,融会贯通)完全可以类比字符串移除那道题!!!dp[t]表示选取情况为t时的最大破损值。
状态压缩dp:
①n很小,不超过20
②组合问题(选取问题——二进制枚举来表示选取情况)
③求最值(直接联想到动态规划)
31.
【蓝桥杯】运用欧几里得求最大公约数解题
https://blog.csdn.net/m0_38033475/article/details/79506342
32.
素数打表。求范围内相邻素数的最小和最大距离
https://blog.csdn.net/m0_38033475/article/details/79508290
33.