目录
头文件与STL
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
vector.insert(vector.begin(),2,99)//在头部插入2个99
vector.erase(vector.begin() + 5, vector.end()) //删除第5个以后的元素
map<string,int>
map.insert(pair<string, int>())
map.count() //0或1
map.earse() //删除
string s;
s.find()
s.substr(int start,int length) //切割子串
//输入含空格字符串
getline(cin,s);
//优先队列
priority_queue<int,vecotr<int>,greater<int>>; //less是降序
python输入
import sys
for line in sys.stdin:
arr = line.split()
list = list(arr[0])
//拼接列表
' '.join(list)
a = int(arr[0])
动态规划
最大数组子串和
dp[i]其实代表的是以i结尾的最大子串和
for(int i=0;i<n;i++){
cin>>a[i];
// 需要额外的ans存储max,因为是子串
dp[i+1]=max(dp[i]+a[i],a[i]);
ans=max(dp[i+1],ans);
}
最长公共子序列
for(int i=1;i<=s1.size();i++){
for(int j=1;j<=s2.size();j++){
if(s1[i-1]==s2[j-1])dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
最长连续公共子串
//t存储公共子串在s1中的末尾位置
int t=0;
//最大长度,要额外的maxLen存储max,因为是子串
int maxLen=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s1[i-1]==s2[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
// =号确保 如果不唯一,则输出s1中的最后一个。
if(dp[i][j]>=maxLen){
maxLen=dp[i][j];
//存储公共子串在s1中的末尾位置,可以输出子串
t=i-1;
}
}
}
}
最长递增子序列
https://www.nowcoder.com/practice/cf209ca9ac994015b8caf5bf2cae5c98?tpId=40&tags=&title=&difficulty=0&judgeStatus=0&rp=1&sourceUrl=
dp[i]只代表以i结尾的最长递增子序列数
for(int i=0;i<n;i++){
//初始化:最长为本身 1
dp[i]=1;
for(int j=0;j<i;j++){
//dp[i]代表以i结尾的最长递增子序列数
if(a[i]>a[j])dp[i]=max(dp[j]+1,dp[i]);
ans=max(dp[i],ans);
}
}
最大上升子序列和
和上述最长递增子序列思路一致,不过dp[i]代表以i结尾的最长递增子序列的和,用ans存储结果
0-1背包
int dp[1001][1001];//代表前i个物体,背包为j的最大价值
int n,bag;
int v[10001],w[10001];
cin>>n>>bag;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i];
}
dp[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=bag;j++){
if(j>=v[i]){
dp[i][j]=max(dp[i-1][j-v[i]]+w[i],dp[i-1][j]);
}
else{
dp[i][j]=dp[i-1][j];
}
}
}
cout<<dp[n][bag];
完全背包
每种物品无限件
for(int i=1;i<=n;i++){
for(int j=v[i];j<=m;j++){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
多重背包问题 I
第 i 种物品最多有 si件,
//将 si拆成多个物品,即01背包
while(s--)
{
a[++t]=v;
b[t]=w;
}//死拆,把多重背包拆成01背包
整数拆分
一个整数总可以拆分为2的幂的和
//奇数
if(i%2)dp[i]=dp[i-1];
//偶数(有1和没1两种情况)
else dp[i]=(dp[i-1]+dp[i/2])%1000000000;
整数划分
计数类DP
一个整数总可以拆乘a1+a2+…的和 (完全背包问题有多少种方案数
***???
dp[0]=1;
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
dp[j]=(dp[j]+dp[j-i])%mod;
}
}
最小邮票
为了集齐n积分,最小需要几张邮票?
dp[0][0]=0;
for(int i=1;i<=m;i++){
//代表集不齐
dp[0][i]=1e9;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(j-a[i]>=0)
dp[i][j]=min(dp[i-1][j-a[i]]+1,dp[i-1][j]);
else
dp[i][j]=dp[i-1][j];
}
}
//如果1e9输入0
最大子矩阵
子矩阵的和:pivot - dp[k-1][j] - dp[i][q-1] + dp[k-1][q-1]
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> matrix[i][j];
//计算机前缀和
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + matrix[i][j];
}
}
int ans = INT_MIN;
//记录最大子矩阵位置
int x1,x2,y1,y2;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int pivot = dp[i][j];
for (int k = 1; k <= i; k++) {
for (int q = 1; q <= j; q++) {
if((pivot - dp[k-1][j] - dp[i][q-1] + dp[k-1][q-1])>ans){
ans = max(ans, pivot - dp[k-1][j] - dp[i][q-1] + dp[k-1][q-1]);
x1=k;
x2=i;
y1=q;
y2=j;
}
}
}
}
}
cout << ans<<endl;
cout<<x1<<y1<<" "<<x2<<y2<<endl;
买卖股票最佳时机
可以买卖多次
// 持有股票和不持有股票
int dp[100001][2];
dp[0][0]=-prices[0];
dp[0][1]=0;
int n=prices.size();
for(int i=1;i<n;i++){
dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]);
dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]);
}
return dp[n-1][1];
数学问题
朴素法筛素数
求n以内的所有素数,时间O(nlog(logn))【不是最优:例如14会被2和7筛重复2次】
void get_primes(int n){
for(int i=2;i<n;i++){
//i被筛了,直接跳过
if(st[i]) continue;
//i是素数,添加进数组,并筛掉与i成倍数的非素数
else {primes[cnt ++ ] = i;
for(int j=2*i;j<=n;j+=i){
//j一定不是素数
st[j]=true;
}
}
}
}
线性筛素数
时间O(n), if(i%primes[j]==0) break;
解决重复筛
for(int i=2;i<=n;i++){
//i没被筛,加入
if(!st[i]) primes[cnt++]=i;
for(int j=0;j<cnt;++j)
{
if(primes[j]*i>n) break;
//翻倍,一个数 * 素数一定为合数
st[primes[j]*i]=true;
//退出循环,避免之后重复进行筛选
if(i%primes[j]==0) break;
}
}
快速幂
int qmi(int a,int b, int p){
if(b==0)return 1 ;
int k = qmi(a,b/2,p)%p;
// k*k可能会超过int
if(b%2==0)return (1LL*k*k) %p;
else return ((1LL*k*k)%p*a)%p;
}
石子合并
区间dp问题
dp[i,j]代表合并i-j的最小代价
s[j]-s[i-1]是合并这两堆的代价
状态转移方程dp[i,j]=min(dp[i,k]+dp[k+1,j]+s[j]-s[i-1])
先枚举距离,再定义左右端点
#include<bits/stdc++.h>
using namespace std;
int main(){
// dp[i][j]表示合并i~j的最小代价
int dp[305][305];
// 前缀和
int s[1001];
int a[1001];
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
s[i]=s[i-1]+a[i];
}
// 处理边界条件
// 先枚举区间长度! 再枚举左端点和右端点
for(int len=2;len<=n;len++){
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
dp[i][j]=1e7;
for(int k=i;k<=j;k++){
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+s[j]-s[i-1]);
}
}
}
cout<<dp[1][n]<<endl;
}
锯木棍
贪心-思想是WPL最小带权路径,永远合并最小的两个
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
//自定义比较结构体
struct cmp{
//函数重载 注意两个括号!!!
bool operator()(int a,int b){
//稳定
if(a==b) return false;
else return a>b;
}
};
int main(int argc, char** argv) {
//priority_queue<int,vector<int>,greater<int>> que;
priority_queue<int,vector<int>,cmp> que;
int n,l;
cin>>n>>l;
int tmp;
int ans=0;
while(n--){
cin>>tmp;
que.push(tmp);
}
while(que.size()!=1){
int a=que.top();
que.pop();
int b=que.top();
que.pop();
que.push(a+b);
ans=ans+a+b;
}
cout<<ans;
return 0;
}
并查集
int Find(int a){
int x=a;
while(s[x]>0){
x=s[x];
}
return x;
}
void Union(int a,int b){
root1=Find(a);
root2=Find(b);
if(root2==root1)return ;
else{
s[root2]=root1;
}
}
Dijkstra单源最短路
int g[N][N]; // 存储每条边
int dist[N]; // 存储1号点到每个点的最短距离
bool st[N]; // 存储每个点的最短路是否已经确定
// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 0; i < n - 1; i ++ )
{
int t = -1; // 在还未确定最短路的点中,确定一个最短的点
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
// 用t更新其他点的距离
for (int j = 1; j <= n; j ++ )
dist[j] = min(dist[j], dist[t] + g[t][j]);
st[t] = true;
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
Python进制转换(整数无限大)
import sys
for line in sys.stdin:
a = line.split()
a=int(a[0])
b=bin(a)
s=(b[2:][::-1])
print(int(s,2))
全排列
回溯法
void dfs(int k){
if(k==n+1){
for(int i=1;i<=n;i++){
cout<<arr[i]<<' ';
}
cout<<'\n';
return ;
}
for(int i=1;i<=n;i++){
//还没访问的数
if(!st[i]){
st[i]=true;
// 存储第k个数
arr[k]=i;
dfs(k+1);
// 恢复-现场
st[i]=false;
}
}
}
int main() {
cin>>n;
dfs(1);
}
神奇的口袋
有一个神奇的口袋,总容积是40,有n个物品,体积为Vi,装满40有多少种装法
void dfs(int u,int j){
if(u==40){
ans++;
}
else{
//从j开始,前面用过的舍弃掉,防止重复
for(int i=j;i<n;i++){
if(!st[i]){
st[i]=true;
dfs(u+v[i],i);
st[i]=false;
}
}
}
}
全排列II
带有重复元素的全排列
while(s[i+1]==s[i])i++; //跳过
void dfs(int k){
if(k==n+1){
for(int i=1;i<=n;i++){
cout<<arr[i]<<' ';
}
cout<<'\n';
return ;
}
for(int i=1;i<=n;i++){
//还没访问的数
if(!st[i]){
st[i]=true;
// 存储第k个数
arr[k]=i;
dfs(k+1);
// 恢复-现场
st[i]=false;
//***当与后一个元素重复时,跳过不排列,且这一步要在恢复现场之后做
while(s[i+1]==s[i])i++;
}
}
}
int main() {
cin>>n;
//使重复的元素排在一起
sort(a,a+n);
dfs(1);
}
放苹果
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?
//处理边界
for(int i=0;i<=m;i++){
//为0的可以不用处理,数组默认为0
//1个盘子的
dp[i][1]=1;
}
for(int i=0;i<=n;i++){
//0个苹果的
dp[0][i]=1;
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
//如果盘子多,多余的用不到的盘子都是没用的
if(j>i){
dp[i][j]=dp[i][i];
}
//如果苹果多,dp[i][j]等于 有空盘子的(挑一个盘子为空)+没有空盘子(每个盘子初始都放一个苹果)的状态
else{
dp[i][j]=dp[i][j-1]+dp[i-j][j];
}
}
}
求第k小
使用快排划分的思想
#include <iostream>
#include <algorithm>
/**求第k小 */
using namespace std;
int n;
int a[10001];
int k;
void partition(int start,int end) {
int pivot=a[start];
int l=start;
int r=end;
while(l<r) {
while(l<r&&a[r]>pivot) {
r--;
}
while(l<r&&a[l]<=pivot) {
l++;
}
if(l<r) swap(a[l],a[r]);
}
swap(a[start],a[l]);
if(l==k-1) {
cout<<a[l];
return ;
}
else if(l<k){
partition(l+1,end);
}
else{
partition(start,l);
}
}
int main(int argc, char** argv) {
cin>>n;
cin>>k;
for(int i=0; i<n; i++) {
cin>>a[i];
}
//数组去重
partition(0,n-1);
return 0;
}
八皇后问题
// u代表行,只需要判断同列,左上和右上角
bool judge(int row,int col){
for(int i=1;i<=8;i++){
if(st[i][col])return false;
}
for(int i=row,j=col;i>=1&&j<=8;i--,j++){
if(st[i][j]){
return false;
}
}
for(int i=row,j=col;i>=1&&j>=1;i--,j--){
if(st[i][j]){
return false;
}
}
return true;
}
void dfs(int u, string s) {
if (u > 8) {
//s输出每一列的位置
arr.push_back(s);
return ;
}
//按行循环
for(int j = 1;j<=8;j++){
if(judge(u, j)){
st[u][j]=true;
dfs(u+1,s+char(j+'0'));
//恢复现场
st[u][j]=false;
}
}
}
哈夫曼编码
priority_queue<int,vector<int>,greater<int> q;
int alpha[26];
//去最小的两个
KMP算法
//字符串下标都从0开始
void getNextTable(int m){
int j=0;
next[0]=-1;
int i=-1;
while(j<m){
if(i==-1 || pattern[j]==pattern[i]){
i++;
j++;
next[j]=i;
}
else{
i=next[i];
}
}
return ;
}
int kMP(string a,string b){
int i=0,j=0;
while(i<n&&j<m){
if(j==-1 || s[i]==pattern[j]){
i++;
j++;
}
else{
j=next[j];
}
}
if(j==m){
return i-j+1;
}
else{
//匹配失败
return -1;
}
}
坠落的蚂蚁
贪心
#include <bits/stdc++.h>
#include <vector>
using namespace std;
using ant = struct node{
int pos;
int v;
};
ant ants[1001];
int main() {
int n;
cin>>n;
int target;
int time=0;
for(int i=0;i<n;i++){
cin>>ants[i].pos>>ants[i].v;
if(ants[i].v==0){
target=i;
}
}
vector<int> antsL;
vector<int> antsR;
for(int i=0;i<n;i++){
if(i!=target){
if(ants[i].pos>ants[target].pos && ants[i].v<0){
antsR.push_back(ants[i].pos);
}
else if(ants[i].pos<ants[target].pos && ants[i].v>0){
antsL.push_back(ants[i].pos);
}
}
}
sort(antsL.begin(),antsL.end());
sort(antsR.begin(),antsR.end());
int l=antsL.size();
int r=antsR.size();
if(l==r) cout<<"Cannot fall!"<<endl;
else if(l>r){
// 抵消r个就够了,从后往前抵消
cout<<100-antsL[l-r-1]<<endl;
}
else{
// 从前往后抵消
cout<<antsR[l]<<endl;
}
}
棋盘游戏
给出起始坐标和终点坐标,DFS遍历图
bool st[10][10];
int a,b,c,d;
int ans=1e8;
int matrix[10][10];
int direct1[4]={1,-1,0,0};
int direct2[4]={0,0,-1,1};
void dfs(int x,int y,int cost,int status){
if(x==c && y==d){
ans=min(ans,cost);
return ;
}
else{
for(int i=0;i<4;i++){
int x1=x+direct1[i];
int y1=y+direct2[i];
if(x1>=0 && y1>=0 && x1<=5 && y1<=5 && !st[x1][y1] ){
st[x][y]=true;
int Acost=matrix[x1][y1]*status;
dfs(x1,y1,cost+Acost,(Acost%4)+1);
st[x][y]=false;
}
}
}
}
快速排序
//划分
int Partition(int a[],int L,int R){
int pivot,i=L,j=R;
pivot=a[L];
while(i<j){
// 先j--再i++,保证最后a[i]<a[L]
while(i<j && a[j]>pivot) j--;
while(i<j && a[i]<=pivot) i++;
if(i<j) swap(a[i],a[j]);
}
//因为一定有a[i]<a[L],所以交换L和i位置的值
swap(a[L],a[i]);
return i;
}
//快排
void Qsort(int a[],int L,int R){
if(L>=R) return ;
int i=Partition(a,L,R);
Qsort(a,L,i-1);
Qsort(a,i+1,R);
}
选择排序
每轮选出最小的交换
for(int i=0;i<n;i++){
int min_tmp=a[i];
int min_index=i;
for(int j=i+1;j<5;j++){
if(min_tmp>a[j]){
min_tmp=a[j];
min_index=j;
}
}
swap(a[i],a[min_index]);
}
冒泡排序
相邻的交换,每轮选出一个最大的
for(int i=0;i<n;i++){
bool is_swap=false;
for(int j=0;j<n-i-1;j++){
if(a[j]>a[j+1]){
swap(a[j],a[j+1]);
is_swap=true;
}
}
//如果本轮没有交换,说明已经是有序了
if(!is_swap)break;
}
插入排序
插入前面已经排序好的序列
for(int i=1;i<n;i++){
int key=a[i];
int j=i-1;
while(j>=0 && key<a[j]){
a[j+1]=a[j];
j--;
}
a[j+1]=key;
}
遍历建立二叉树
TNode(char c):data©,left(nullptr),right(nullptr){};
using TreeNode = struct TNode{
char data;
struct TNode* left;
struct TNode* right;
TNode(char c):data(c),left(nullptr),right(nullptr){};
};
TreeNode* Build(TreeNode* root,char c){
if(c=='#')return NULL;
// C style:(TreeNode*)malloc(sizeof(TreeNode))
root=new TNode(c);
char c1=s[cnt++];
root->left=Build(root->left,c1);
char c2=s[cnt++];
root->right=Build(root->right,c2);
return root;
}
void Inorder(TreeNode* root){
if(root->left)
Inorder(root->left);
cout<<root->data<<endl;
if(root->right)
Inorder(root->right);
}
void postOrder(TreeNode* root){
}
int main(int argc, char** argv) {
TreeNode* T=NULL;
T=Build(T,s[cnt++]);
Inorder(T);
return 0;
}
中缀表达式求值
不带括号版
stack<double> nums;
stack<char> ops;
for (int i = 0; i < s.size(); i++) {
if (isdigit(s[i])) {
//处理10以上的数字
if( i!=0 && isdigit(s[i-1])){
double x=nums.top();
nums.pop();
nums.push(x*10+(s[i]-'0'));
}
else{
nums.push(s[i]-'0');
}
} else {
if (ops.empty()) {
ops.push(s[i]);
} else {
//栈内符号优先级 大于等于 当前符号,则弹出 -getPriority判断符号优先级
while (!ops.empty() && getPriority(s[i]) <= getPriority(ops.top())) {
char c=ops.top();
ops.pop();
double a = nums.top();
nums.pop();
double b = nums.top();
nums.pop();
if (c == '*')
nums.push(b * a);
else if (c == '/')
nums.push(b / a);
else if (c == '+')
nums.push(b + a);
else if (c == '-')
nums.push(b - a);
}
ops.push(s[i]);
}
}
}
while(!ops.empty()){
char c = ops.top();
ops.pop();
double a = nums.top();
nums.pop();
double b = nums.top();
nums.pop();
if (c == '*')
nums.push(b * a);
else if (c == '/')
nums.push(b / a);
else if (c == '+')
nums.push(b + a);
else if (c == '-')
nums.push(b - a);
}
cout << nums.top() << endl;
求最小生成树Kruskal
//1.对边排序
//2.选最小边
for(int i=0;i<n;i++){
int a=edge[i].a;
int b=edge[i].b;
int w=edge[i].w;
if(find(a)!=find(b)){
ans+=w;
union(a,b);
}
}