LQ2

96 阅读25分钟

1. 地宫取宝 *

www.lanqiao.cn/problems/21…

#include <iostream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <cstring>
#define F(i,a,b) for(int i = a; i < b; i++)
#define FF(i,a,b) for(int i = a; i > b; i--)
#define ll long long
#define pragma GCC optimize(2)
#define N 80
const int M = 1e9 + 7;
using namespace std;
int n, m, K;
int dp[N][N][N][N];
int a[N][N];
void dfs(int x, int y) {
	
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin >> n >> m >> K;
	for(int i = 1; i <= n; ++i) {
		for(int j = 1; j <= m; ++j) {
			cin >> a[i][j];
			a[i][j]++;
		}
	}
	for(int i = 1; i <= n; ++i) {
		for(int j = 1; j <= m; ++j) {
			cout << a[i][j] << " ";
		}
		cout << '\n';
	}
//	//初始化
//	dp[1][1][1][a[1][1]] = 1;
//	dp[1][1][0][0] = 1;
//	for(int i = 1; i <= n; ++i) {
//		for(int j = 1; j <= m; ++j) {
//			for(int k = 0; k <= K; ++k) {
//				for(int w = 0; w <= 13; ++w) {
//					dp[i][j][k][w] = (dp[i][j][k][w] % M + dp[i-1][j][k][w] % M) % M;
//					dp[i][j][k][w] = (dp[i][j][k][w] % M + dp[i][j-1][k][w] % M) % M;
//					if(w == a[i][j]) {
//						for(int s = 0; s < a[i][j]; ++s) {
//							dp[i][j][k][w] = (dp[i][j][k][w] % M
//							+ dp[i-1][j][k-1][s] % M) % M;
//
//							dp[i][j][k][w] = (dp[i][j][k][w] % M
//							+ dp[i][j-1][k-1][s] % M) % M;
//						}
//					}
//				}
//			}
//		}
//	}
//	ll res = 0;
//	for(int i = 1; i <= 13; ++i)
//		res = (res + dp[n][m][K][i]) % M;
//	cout << res;
	return 0;
}

2.最长上升子序列长度

leetcode-cn.com/problems/lo…

时间复杂度O(N^2)

思路:子问题中最大值就是该问题的解,dp[i]表示以第i个数为终点所得到的最长上升子序列长度,每次发现终点的值比起点的值大,就更新dp[i],最终从dp[i]里寻最大值

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <queue>
#include <sstream>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
using namespace std;
const unsigned int M = 1e9 + 7;
int n;
const int N = 10086;
int a[N];
int dp[N];
int main() {
	cin >> n;
	for(int i = 1; i <= n; ++i) {
		cin >> a[i];
		dp[i] = 1;
	}
	//j是终点
	for(int j = 2; j <= n; ++j) {
		//i是起点, [i,j)
		for(int i = 1; i < j; ++i) {
			//如果终点所指向的值大于起点所指向的值,就更新dp数组
			if(a[j] > a[i]) {
				dp[j] = max(dp[j], dp[i] + 1);
			}
		}
	}
	//遍历dp数组,输出最大的值
	cout << * max_element(dp+1, dp+1+n);
	return 0;
}

3.最长公共子序列

leetcode-cn.com/problems/lo…

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
using namespace std;
const unsigned int M = 1e9 + 7;
int n;
const int N = 10086;
char s1[N],s2[N];
//maxLen(i,j)表示s1左边i个字符形成的子串和s2左边j个字符形成的子串的最长公共子序列的长度
int maxLen[N][N]; 
int main() {
	while(cin >> s1 >> s2) {
		int l1 = strlen(s1);
		int l2 = strlen(s2);
		int i, j;
		//初始化,当s1为空串且s2不为空串,就将maxLen[i][0]初始化为0
		for(i = 0; i <= l1; ++i)
			maxLen[i][0] = 0;
		//初始化,当s2为空串且s1不为空串,就将maxLen[0][j]初始化为0
		for(j = 0; j <= l2; ++j)
			maxLen[0][j] = 0;
		for(i = 1; i <= l1; ++i) {
			for(j = 1; j <= l2; ++j) {
				if(s1[i-1] == s2[j-1])
					maxLen[i][j] = maxLen[i-1][j-1]+1;
				else
					maxLen[i][j] = max(maxLen[i][j-1],maxLen[i-1][j]);
			}
		}
		cout << maxLen[l1][l2] << endl;
	}
	return 0;
}

4. 路径 *

思路:dijkstra最短路径 www.lanqiao.cn/problems/14…

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
using namespace std;
int n,m;
const int N = 3000;
#define INF 0x3f3f3f3f
int g[N][N];
int dist[N]; //存储最短距离
bool st[N];  //判断能否找到当前最短距离的那个点
int dij() {
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0; //自己到自己是0
	for(int i = 0; i < n-1; ++i) {
		int t = -1;
		for(int j = 1; j <= n; ++j) {
			//如果当前的j没有出现
			if(!st[j] && (t == -1 || dist[j] < dist[t]))
				t = j;
		}
		//1~n更新距离
		for(int j = 1; j <= n; ++j) {
			dist[j] = min(dist[j], dist[t] + g[t][j]);
		}
		st[t] = true; //标记t找到了
	}
	//最后判断
	if(dist[n] == INF) return -1;
	return dist[n];
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n; //2021
	memset(g,0x3f,sizeof g);
	for(int i = 1; i <= 2021; ++i) {
		for(int j = 1; j <= 2021; ++j) {
			if(abs(i-j) <= 21) //存在一条边
			{
				int w = lcm(i,j);//最小公倍数,权值
				g[i][j] = w;
			}
		}
	}
	cout << dij();
	return 0;
}

5.dijkstra *

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 2022;
/*
 n个点m条边的有向图,所有边权为正值
 求出1号点到n号点的最短距离,如果无法从
 1号点走到n号点,则输出-1
*/
int n, m;
int g[N][N];;
int dist[N]; //用于存储每个点到起点的最短距离
int st[N]; //用于在更新最短距离时,判断当前的点的最短距离是否确定
int dij() {
	//初始化距离
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0; //第一个点到自身的距离为0
	//有n个点,所以要迭代n次
	for(int i = 0; i < n; ++i) {
		int t = -1; //t存储当前访问的点
		//这里的j代表从1号点开始
		for(int j = 1; j <= n; ++j) {
			//不在st集合且没有更新过或发现更短的路径
			if(!st[j] && (t == -1 || dist[j] < dist[t])) t = j;
		}
		//加入到st集合中
		st[t] = true;
		
  		//依次更新每个点到相邻的点的路径值
  		//找到了距离最小的点t,并用最小的点t去更新其他的点到起点的距离
			for(int j = 1; j <= n; ++j) {
				dist[j] = min(dist[j], dist[t] + g[t][j]);
			}
		}
		// 如果起点到达不了n号节点,则返回-1
			//如果n个点的路径为无穷大则不存在最短路径
			if(dist[n] == INF) return -1;
			// 返回起点距离n号节点的最短距离
			return dist[n];
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n >> m;
	//初始化图为最大值
	memset(g,0x3f,sizeof g);
	while(m--) {
		int x, y, z;
		cin >> x >> y >> z;
		//如果发生重边的情况则保留最短的一条边
		g[x][y] = min(g[x][y], z);
	}
	cout << dij() << endl;
	return 0;
}

6.时间显示

逆运算+取模+printf("%02d")

#include <iostream>
#define ll long long
using namespace std;
int main()
{
  ll n;
  cin >> n;
  ll sum = n / 1000; //将输入的毫秒转换成秒, 所以除以1000, 1秒 = 1000毫秒
  int h = (sum / 3600) % 24; //秒转换成分钟除以60,再转换成小时,再除以60,最后%24是为了当h等于24时变成0
  int m = (sum / 60) % 60; //分钟同理
  int s =   sum % 60; //秒也同理
  printf("%02d:%02d:%02d",h,m,s);
  return 0;
}

7.卡片 *

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const unsigned int M = 1e9 + 7;
int a[10];
bool check(int n) {
	while(n) {
		int now = n % 10;
		if(a[now] > 0) a[now]--;
		else    return false;
		n /= 10;
	}
	return true;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	//0到9各有2021张,共20210张
	for(int i = 0; i < 10; ++i) a[i] = 3;
	for(int i = 1; ; ++i) {
		if(!check(i)) {
			cout << i - 1 << endl; //11不符合,所以输出i-1
			break;
		}
	}
	return 0;
}

8.约数个数 *

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const unsigned int M = 1e9 + 7;
int a[100];
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int num = 1200000;
	int res = 1;
	for(int i = 2; i <= num; ++i) {
		while(num % i == 0) {
			a[i]++;
			num /= i;
		}
	}
	for(int i = 0; i < 100; ++i) {
		if(a[i])
			res *= a[i] + 1;
	}
	cout << res;
	return 0;
}

9.门牌制作

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const unsigned int M = 1e9 + 7;
int a[100];
int get2(int n) {
	int res = 0;
	while(n) {
		if(n % 10 == 2) {
			++res;
		}
		n /= 10;
	}
	return res;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int res = 0;
	for(int i = 1; i <= 2020; ++i) {
		res += get2(i);
	}
	cout << res;
	return 0;
}

10. 跑步锻炼

枚举年月日+构造天数数组

#include <iostream>
using namespace std;
int day[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
bool isLeapY(int y) {
  if((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) return true;
  return false;
}
int main()
{
  int today = 6, res = 0; //toady表示周6 
    //枚举年
  for(int y = 2000; y <= 2020; ++y) {
      //如果是闰年,就将二月份修改为29天
      if(isLeapY(y)) {
         day[2] = 29;
      } else {
         day[2] = 28;
      }
      //枚举月份, 2000年到2019年从1月份枚举到12月份
      //但2020年只用枚举到10月份
      for(int m = 1; m <= (y == 2020 ? 10 : 12); ++m) {
          //如果是2020年且10月份,10月份天数就改为1
          if(y == 2020 && m == 10) {
             day[m] = 1;
          }
          //枚举天数, 从第一天~根据具体月份枚举
          for(int d = 1; d <= day[m]; ++d) {
             //如果满足某天是周一或月初(1日), 就跑2千米,否则,就跑1千米
             if(d == 1 || today == 1) res += 2;
             else res += 1;

             //更新today
             today = (today + 1) % 7;
          }
      }
  }
  cout << res;
  return 0;
}

11.单词分析 *

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const unsigned int M = 1e9 + 7;
int cnt[30], _max = 0, ch;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	string s;
	cin >> s;
	for(auto i : s) {
		cnt[i - 'a']++; //统计每个字母出现次数
	}
	//得到出现次数最多的字母与次数
	for(int i = 0; i < 26; ++i) {
		if(_max < cnt[i]) {
			_max = cnt[i];
			ch = i;
		}
	}
	cout << (char)(ch + 'a') << '\n';
	cout << _max << '\n';
	return 0;
}

12.数字三角形 dp *

www.lanqiao.cn/courses/549…

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 300;
int a[N][N], dp[N][N];
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	memset(dp, -0x3f, sizeof dp);
	int n;
	cin >> n;
    for(int i = 1; i <= n; ++i) {
    	for(int j = 1; j <= i; ++j) {
    		cin >> a[i][j];
		}
	}
	dp[1][1] = a[1][1];
	for(int i = 2; i <= n; ++i) {
		for(int j = 1; j <= i; ++j) {
			dp[i][j] = max(dp[i-1][j-1], dp[i-1][j]) + a[i][j];
		}
	}
	if((n - 1) & 1)
		cout << max(dp[n][1 + (n - 1) / 2], dp[n][1 + (n - 1) / 2 + 1]) << endl;
	else
		cout << dp[n][1 + (n - 1) / 2] << endl;
	return 0;
}

13. 年号子串

www.lanqiao.cn/courses/549… 字符串反转+预处理

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 3000;
char s[26];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	char c = 'A';
	for(int i = 0; i < 26; ++i) {
		s[i] = c++;
	}
	int n = 2019;
	string res = "";
	while(n--) {
		int tmp = n % 26;
		res += s[tmp];
		n /= 26;
	}
	reverse(res.begin(), res.end());
	cout << res << endl;
	
	return 0;
}

14. 矩阵旋转

www.lanqiao.cn/courses/549…

#include <iostream>
#include <algorithm>
#define F(i,a,b) for(int i = a; i < b; i++)
using namespace std;
int n, m;
const int N = 210;
int a[N][N];
/*
		0  1 2 3

	0	-1 3 6 3
	1	 7 7 9 1
	2	10 3 4 6

	0	10 7 -1
	1	3  7 3
	2	4  9 6
	3	6  1 3
	
	3行4列变成4行3列
    新[0][0] = 旧[2][0], 新[0][1] = 旧[1][0], 新[0][2] = 旧[0][0][1][0] = 旧[0][1], 新[1][1] = 旧[1][1], 新[1][2] = 旧[2][1]
    以此类推 => 新[i][j] = 旧[j][i]
*/
int main()
{
	cin >> n >> m;
	F(i,0,n) {
		F(j,0,m) {
			cin >> a[i][j];
		}
	}
	
	
	F(j,0,m) {
		for(int i = n-1; i >= 0; i--) {
			if(i == 0) {
				cout << a[i][j];
				cout << endl;
			} else
				cout << a[i][j] << " ";
		}
	}
	return 0;

}

15.等差数列

坑点:当公差d为0时,直接输出n

方法一

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 100010;
int a[N];
int dd[N];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	ll n;
	cin >> n;
	F(i,0,n) cin >> a[i];
	sort(a, a+n);
	int d = a[1] - a[0];
	for(int i = 1; i < n - 1; ++i) {
		d = min(d, a[i+1] - a[i]);
	}
	if(d != 0) {
		int res = (a[n-1] - a[0]) / d + 1;
		cout << res;
	} else
		cout << n;
//	cout << d;

	return 0;
}

方法二

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 100000;
ll a[N];
ll dd[N];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin >> n;
    F(i,0,n) cin >> a[i];
    sort(a, a+n);
    ll _max = a[0], _min = a[n-1], d;
    for(int i = 0; i < n - 1; ++i) {
        dd[i] = a[i+1] - a[i];
    }
    sort(dd, dd+n-1);
    d = dd[0];
//    cout << d << endl;
    ll res = abs(_max - _min);
    res /= d;
    if(d != 0) {
    	cout << res + 1;
	} else cout << n;
//    cout << _max << " " << _min << endl;

    return 0;
}

16.扫地机器人

贪心+二分

www.lanqiao.cn/courses/549…

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 1e5+10;
int a[N]; //机器人位置
int n, k;
bool check(int len) {
	int sweep = 0; //sweep代表清扫到了哪个位置
	for(int i = 1; i <= k; ++i) {
		if(a[i] - len <= sweep) //如果当前机器人只扫左侧,能够覆盖左侧未清扫的位置
		{
			if(a[i] <= sweep) //如果当前机器人已经处于清扫过的位置
				sweep = a[i] + len - 1;
			else  //否则从上一个清扫到的位置继续
				sweep += len;
		}
		else { //当前机器人只扫左侧,不能覆盖左侧未清扫的位置
			return false;
		}
	}
	return sweep >= n; //表示当前方案可行
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
   cin >> n >> k;
   for(int i = 1; i <= k; ++i) cin >> a[i];
   //二分+贪心,首先对机器人的位置进行排序
   sort(a+1, a+1+k);
   int l = 0, r = n, mid, res;
   //左闭右闭
   while(l <= r) { //二分清扫范围
		mid = l + (r - l >> 1);
		//如果当前方案可行,则缩小清扫范围,试图寻找更小的方案
		if(check(mid)) {
			r = mid - 1;
			res = mid;
		} else {
			l = mid + 1;
		}
   }
   cout << (res - 1)*2 << endl;
    return 0;
}

17.人物相关分析

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 1e5+10;
int k;
string s;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
	cin >> k;
	cin.get();
 	getline(cin,s);
 	vector<pair<bool,int>> v; //用于记录合法的Alice和Bob的位置
 	for(int i = 0; i < s.size(); ) {
 		if(s.substr(i, 5) == "Alice") {
 			if(i - 1 >= 0 && isalpha(s[i - 1]) || i + 5 < s.size() && isalpha(s[i + 5]))
				continue;
			v.push_back({0,i});  //用0代表Alice
			i += 5;
		 } else if(s.substr(i,3) == "Bob") {
		 	if(i - 1 >= 0 && isalpha(s[i-1]) || i + 3 < s.size() && isalpha(s[i + 3]))
				continue;
			v.push_back({1,i});  //用1代表Bob
			i += 3;
		 } else i++;
	 }
	 ll cnt = 0, c[2] = {}; //c[0]代表区间内有多少个alice,c[1]代表区间内有多少个bob
	 for(int l = 0, r = 0; r < v.size(); ++r) { //遍历记录alice和bob位置的v数组
		 c[v[r].first]++; //当前位置时alice,则c[0]++, 是bob,则c[1]++
		 //找到在k范围内与v[r]距离最远的另一个人名
		 while(l < r && v[r].second - v[l].second - (v[l].first ? 3 : 5) > k)
		    c[v[l].first]--, l++;
		cnt += c[v[r].first ^ 1];
	 }
	 cout << cnt << endl;
    return 0;
}

18. 星期一

www.lanqiao.cn/problems/61…

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 1e5+10;
int days[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
bool isLeapY(int y) {
	return ((y % 4 == 0 && y % 100 != 0) 
			|| y % 400 == 0);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
	int today = 2, res = 0;
	for(int y = 1901; y <= 2000; ++y) {
		if(isLeapY(y)) {
			days[2] = 29;
		} else {
			days[2] = 28;
		}
		for(int m = 1; m <= 12; ++m) {
			for(int d = 1; d <= days[m]; ++d) {
				if(today == 1) res++;
				today = (today + 1) % 7;
			}
		}
	}
	cout << res;
    return 0;
}

19. 分数

www.lanqiao.cn/problems/61…

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
	int fm = 1, fz = 1;
	for(int i = 1; i < 20; ++i) {
		fm *= 2;
		fz += fm;
	}
	printf("%d/%d",fz,fm);
    return 0;
}

20.乘积尾0

思路:找出多少个相乘能等于0的,需要计算出里面有多少个数是包含五和二的,然后比较找出有多少个5和2可以相乘等于0,然后输出

www.lanqiao.cn/courses/549…

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
int cnt2, cnt5;
int a[100];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
	for(int i = 0; i < 100; ++i) {
		cin >> a[i];
		while(a[i] % 2 == 0) {
			cnt2++;
			a[i] /= 2;
		}
		while(a[i] % 5 == 0) {
			cnt5++;
			a[i] /= 5;
		}
	}
	cout << min(cnt2, cnt5) << endl;
    return 0;
}

21.数列求值

www.lanqiao.cn/courses/549… 在运算内取模

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    ll n = 20190324;
	ll a=1,b=1,c=1,d;
	for(int i = 4; i <= n; ++i) {
		d = (a + b + c) % 10000;
		a = b;
		b = c;
		c = d;
	}
	cout << d;
    return 0;
}

22.阶乘计算

www.lanqiao.cn/courses/549…

#include<iostream>
#include<string.h>
#define N 2000100
typedef long long ll;
using namespace std;
int a[N];
//数组a用来存储阶乘后值的每一位
int k = 1;
//k代表阶乘后的值的位数
void cheng(int x)
{
	int ch = 0;
	//利用一个ch用来存储进位
	for(int i = 1; i <= k; i++)
	{//循环做乘法
	//这里也可以从2开始,因为乘1结果都是一样的
		a[i] = a[i] * x +ch;
		//每一位都要乘x,同时加上进位
		ch = a[i] / 10;
		a[i] = a[i] % 10;
		//这里a[i]留下个位数的数值
		//剩下的进位
	}
	while(ch > 0)
	{//假如还有进位没完成,就需要位数自加
	//然后存储上进位的值
		a[k+1] = ch % 10;
		ch /= 10;
		k++;
	}
}
int main()
{
	int n;
	scanf("%d",&n);
	a[1] = 1;
	//数组第一位一定要赋值为1,不然任何数乘0都是0
	for(int i = 1; i <= n; i++)
		cheng(i);

	for(int i = k; i >= 1; i--)
	{//逆序输出
		printf("%d",a[i]);
	}
	return 0;
}

23.拼数

www.lanqiao.cn/courses/549…

a+b > b+a, 字符串默认按首字母依次按ASCII码排序

#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
const int N = 25;
bool cmp(string a, string b) {
	return a+b > b+a;
}
int n;
string s[N];
int main()
{
  cin >> n;
  for(int i = 0; i < n; ++i) {
  	cin >> s[i];
  }
//  for(int i = 1; i <= n-1; ++i) {
//  	for(int j = 0; j < n - i; ++j) {
//  		if(s[j] < s[j + 1])
//			swap(s[j],s[j+1]);
//	  }
//  }
  sort(s,s+n,cmp);
  for(int i = 0; i < n; ++i) {
  	cout << s[i];
  }
  return 0;
}

24.统计数字

www.lanqiao.cn/courses/549…

  1. 使用桶排序的思想,开一个和数字大小一致的数组;但是数字的范围是10^9,因此这里是不可行的。
  2. 使用map,key对应着数字,value表示出现频率,最后用迭代器遍历一遍即可。
  3. 书写二叉排序树,节点记录值得同时记录下出现的频率;最后进行一遍中序遍历

方法一:map<int,int>

key记录元素值,val记录出现的次数

#include <iostream>
#include <algorithm>
#include <string>
#include <set>
#include <map>
#define ll long long
using namespace std;
const int N =  1e4;


int main()
{
  map<int,int> mp;
  int n;
  cin >> n;
  for(int i = 1; i <= n; ++i) {
	 int x;
	 cin >> x;
	 mp[x]++;
  }
  for(auto &i : mp) {
  	cout << i.first << " " << i.second << endl;
  }
  return 0;
}

25.付账问题

www.lanqiao.cn/courses/549…

#include <iostream>
#include <algorithm>
#include <string>
#include <set>
#include <cmath>
#include <map>
#define ll long long
using namespace std;
const int N =  1e7;
int a[N];
int main()
{
  ios_base::sync_with_stdio(false);
  cin.tie(NULL); cout.tie(NULL);
  ll n, S; //人数与总金额
  double res = 0, avg;
  cin >> n >> S;
  avg = 1.0 * S / n; //总平均值是固定不变的
  for(int i = 0; i < n; ++i) cin >> a[i];
  sort(a,a+n);
  for(int i = 0; i < n; ++i) {
    //如果钱的数额*分摊的人数小于总额,说明当前的数额小于当前均值=>全部贡献出来
    if(a[i] * (n - i) < S) {
      res += (a[i] - avg) * (a[i] - avg); //累加到方差
      S -= a[i]; //应支付的总额变小
    }  else { //当前及后序每个人的数额都超出当前均值
       double cur_avg = 1.0 * S / (n - i); //算出一个当前均值=>每个人应该付的数额
       res += (avg - cur_avg) * (avg - cur_avg) * (n - i);
       break;
    }
  }
  printf("%.4lf",sqrt(res / n));
  return 0;
}

26.日志统计

www.lanqiao.cn/courses/549…

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
  //桶排序
  int N,D,K; //N行,D时间段,不少于k个赞
  cin >> N >> D >> K;
  vector<int> v(N);
  vector<vector<int>> nums(100005); //10^5
  int ts, id; //在ts时刻编号id的帖子
  for(int i = 0; i < N; ++i) {
    cin >> ts >> id;
    nums[id].push_back(ts);
  }
  for(int i = 0; i < 100005; ++i) {
    //特殊情况
    if(K == 1 && nums[i].size() >= 1) {
      cout << i << endl;
      continue;
    }
    if(nums[i].size() >= 1) {
      int out = 1;
      bool backnow = false;
      for(int j = 0; j < nums[i].size(); ++j) {
        for(int m = 0; m < nums[i].size() && m != j; ++m) {
           if(abs(nums[i][j] - nums[i][m]) < D) {
             if(++out >= K) {
               cout << i << endl;
               backnow = true;
               break;
             }
           }
        }
          if(backnow) break;
      }
     
    }
  }
  return 0;
}

27. 锻造兵器

非AC代码

#include <iostream>
#include <unordered_map>
using namespace std;
#include <vector>
#include <algorithm>
const int N = 1e5 * 3;
int a[N];

int main()
{
   //n块锻造石,第i块锻造石的属性值为ai
   //从n块锻造石任取2块来搞兵器
   //当2块锻造石的属性值差值等于C,才算成功
   //求有多少种选取锻造石的方案才可以成功
   int n, C, res = 0; // 1<=n<=2*10^5; 0<=C<=10^9
   cin >> n >> C;
    unordered_multimap<int, int> umap;
   for(int i = 0; i < n; ++i) {
   		cin >> a[i];
//		umap[a[i]] = i;
		umap.insert({a[i], i});
   }

   /* O(N^2)超时
   for(int j = 0; j < n; ++j) {
	  for(int i = j+1; i < n; ++i) {
			if(abs(a[i] - a[j]) == C) res++;
	  }
   }
   */
//   for(auto it = umap.begin(); it != umap.end(); ++it) {
//	 cout << it->first << " " << it->second << endl;
//   }
   for(int i = 0; i < n; ++i) {
	 if(a[i] < C) continue;
	 int sum = C + a[i];
	 res += umap.count(sum);
   }
   cout << res;
    return 0;
}

AC代码

三指针尺取法

#include <iostream>
#include <unordered_map>
using namespace std;
#include <vector>
#include <algorithm>
#define ll long long
const int N = 1e5 * 3;
int a[N];

int main()
{
   //n块锻造石,第i块锻造石的属性值为ai
   //从n块锻造石任取2块来搞兵器
   //当2块锻造石的属性值差值等于C,才算成功
   //求有多少种选取锻造石的方案才可以成功
   int n, c; // 1<=n<=2*10^5; 0<=C<=10^9
   cin >> n >> c;
   for(int i = 0; i < n; ++i) {
   		cin >> a[i];
   }
   sort(a,a+n);
   ll res = 0;
   for(int i=0,j=0,k=0; i < n; ++i) {
		while(j < n && a[j] - a[i] < c)
			j++; //用j、k查找数字相同的区间
		while(k < n && a[k] - a[i] <= c)
			k++; //区间[j,k]内所有数字相同
		if(a[j] - a[i] == c && a[k - 1] - a[i] == c && k - 1 >= 1)
			res += k - j; //统计数对
   }
   cout << res << endl;
    return 0;
}

28. 二分模板

image.png

#include <iostream>
#include <unordered_map>
using namespace std;
#include <vector>
#include <algorithm>
#define ll long long
const int N = 1e5 * 3;
/*
    二分法妙用:
        单调递增序列种找x或x的后继,即在
        单调递增数列a[]种查找某个元素x,如果
        数列中没有x,则找比它大的下一个数
        下面是左闭右开 [0,n)区间的模板
*/
int binarySearch(int* a, int n, int val) {
//a[0] ~ a[n-1] 是单调递增的, 搜索总次数是log(n)
	int l = 0, r = n; //左闭右开[0,n),不是n-1
	while(l < r) { //直到left == right结束
		const int mid = l + (r - l >> 1);
		//说明val在mid左边,新的搜索区域是左半部分,left不变,right更新为mid
		if(a[mid] >= val) r = mid;
//当a[mid] < val时,说明val在mid右边,新的搜区域是右半部分,right不变,left更新为mid+1
		else    l = mid + 1;
	}
	return l;
	//特殊情况:a[n-1] < x时,返回n
//	 if(a[n-1] < val) return n;
}
int main()
{
	int a[] = {1,3,5,7,9,11,13};
	int n = sizeof(a) / sizeof(int);
	int pos = binarySearch(a,n,7);
	cout << pos;
    return 0;
}

29.分巧克力(二分)

方法一:暴力(超时)

n 个长方形,长方形的最大边长 DD,复杂度是 O(n×D),而 n 和 D 的最大值是 10^6,暴力法会超时。 www.lanqiao.cn/problems/99…

#include <iostream>
#include <unordered_map>
using namespace std;
#include <vector>
#include <algorithm>
#define ll long long
/*
    二分法妙用:
        单调递增序列种找x或x的后继,即在
        单调递增数列a[]种查找某个元素x,如果
        数列中没有x,则找比它大的下一个数
        下面是左闭右开 [0,n)区间的模板
*/
const int N = 1e5+10;
int h[N];
int w[N];
int n, k;
bool check(int d) { //检查够不够分
	int num = 0;
	for(int i = 0; i < n; ++i)
		num += (h[i]/d)*(w[i]/d);
	if(num >= k) return true; //够分
	else return false; //不够分
}
int main()
{
	cin >> n >> k;
	for(int i = 0; i < n; ++i) {
		cin >> h[i] >> w[i];
	}
	int d = 1; //正方形边长, 从1枚举到最大边长,直到刚好够分为止
	while(1) {
		if(check(d)) d++;
		else    break;
	}
	cout << d - 1;
    return 0;
}

方法二:二分1

#include <iostream>
#include <unordered_map>
using namespace std;
#include <vector>
#include <algorithm>
#define ll long long
/*
    二分法妙用:
        单调递增序列种找x或x的后继,即在
        单调递增数列a[]种查找某个元素x,如果
        数列中没有x,则找比它大的下一个数
        下面是左闭右开 [0,n)区间的模板
*/
const int N = 1e5+10;
int h[N];
int w[N];
int n, k;
bool check(int d) { //检查够不够分
	int num = 0;
	for(int i = 0; i < n; ++i)
		num += (h[i]/d)*(w[i]/d);
	if(num >= k) return true; //够分
	else return false; //不够分
}
int main()
{
	cin >> n >> k;
	for(int i = 0; i < n; ++i) {
		cin >> h[i] >> w[i];
	}
	//猜d的取值,即对边长d的取值范围二分,
	//	将复杂度O(D)优化到了O(logD)
	/*
		1. 开始时d的范围是1到d,试试中间值D/2
		如果这个值大了,就把范围缩小为1到D/2
		如果这个值小了,就把范围缩小为D/2到D
		2. 取新的中间值 D/4, 再试...
        直到找到合适的值为止
	*/
	//如果整数二分法的left、right、mid处理不当,会出现死循环
	//写法一
	int l = 1, r = 1e5+10;
	while(l < r) {
//		const int mid = l + (r - l >> 1); //死循环
		const int mid = l + (r - l >> 1) + 1; //要加1
//		const int mid = (l + r + 1) >> 1;
		if(check(mid))
			l = mid; //新的搜索区间是右半部分,所以r不变,调整l=mid
		else
			r = mid-1; //新的搜索区间是左半部分,所以l不变,调整r=mid-1
	}
	cout << l;
    return 0;
}

方法三:二分二

#include <iostream>
#include <unordered_map>
using namespace std;
#include <vector>
#include <algorithm>
#define ll long long
/*
    二分法妙用:
        单调递增序列种找x或x的后继,即在
        单调递增数列a[]种查找某个元素x,如果
        数列中没有x,则找比它大的下一个数
        下面是左闭右开 [0,n)区间的模板
*/
const int N = 1e5+10;
int h[N];
int w[N];
int n, k;
bool check(int d) { //检查够不够分
	int num = 0;
	for(int i = 0; i < n; ++i)
		num += (h[i]/d)*(w[i]/d);
	if(num >= k) return true; //够分
	else return false; //不够分
}
int main()
{
	cin >> n >> k;
	for(int i = 0; i < n; ++i) {
		cin >> h[i] >> w[i];
	}
	//猜d的取值,即对边长d的取值范围二分,
	//	将复杂度O(D)优化到了O(logD)
	/*
		1. 开始时d的范围是1到d,试试中间值D/2
		如果这个值大了,就把范围缩小为1到D/2
		如果这个值小了,就把范围缩小为D/2到D
		2. 取新的中间值 D/4, 再试...
        直到找到合适的值为止
	*/
	//如果整数二分法的left、right、mid处理不当,会出现死循环
	//写法二
	int l = 1, r = 1e5+10;
	while(l < r) {
		const int mid = l + (r - l >> 1);
//		const int mid = l + (r - l >> 1) + 1; //导致死循环
		if(check(mid))
			l = mid + 1; //新的搜索区间是右半部分,所以r不变,调整l=mid+1
		else
			r = mid; //新的搜索区间是左半部分,所以l不变,调整r=mid
	}
	cout << l - 1;
    return 0;
}

30.跳石头(二分)

#include <bits/stdc++.h>
using namespace std;
/*
    选2块岩石作为比赛起点和终点
    在比赛期间,有N块岩石(不包含起点和终点的岩石)
    选手们将从起点出发,每一步跳向相邻的岩石,
    直到到达终点。

    为了提高比赛难度,组委会计划移走一些岩石,使
	得选手们在比赛过程中的最短跳跃距离尽可能长。
	由于预算限制,组委会至多从起点和终点之间移走M块岩石
	(不能移走起点和终点的岩石)。

    输入L,N,M 分别表示别表示起点到终点的距离,
	起点和终点之间的岩石数,以及组委会至多移走的岩石数。
	接下来N行整数Di(0 < Di < L)表示第
	i块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,
	且不会有两个岩石出现在同一个位置。

	数据范围:0<=M<=N<=5*10^4, 1<=L<=10^9

    二分套路题:最小值最大化; 类似的有最大值最小化

    简化题意:在n块岩石中移走m个石头,有许多种移动方法。
    在第i种移动方法种,剩下的石头之间的距离,有一个最小
    距离ai。在所有移动方法的最小距离ai中,问最大的
    ai是多少。【最小值最大化】
    不能直接暴力找所有组合情况,会超时。
    在n块岩石中选m个石头,有n!/(m!)*(n-m)!种组合
    转换思路,不去找搬走石头的各种组合,而是给出
    一个距离d,检查能不能搬走m块石头得到最短距离d

    把所有的d都试一遍,肯定能找到一个最短的d,而用
    二分可以快速找到
*/
const int maxn = 1e4*6;
int L,N,M;
int stone[maxn]; //岩石
bool check(int d) { //检查距离d是否合适
	int sum = 0; //sum记录搬走石头的数量
	int pos = 0; //当前站立的石头
	for(int i = 0; i < N; ++i) {
		if(stone[i] - pos < d) //第i块石头可以搬走
			sum++;
		else
			pos = stone[i]; //第i块石头不能搬走
	}
	if(sum <= M) return true; //要移动的石头比m小,满足条件
	else return false; //要移动的石头比m多,不满足条件
}
int main() {
    cin >> L >> N >> M;
	for(int i = 0; i < N; ++i) cin >> stone[i];
	int l = 0, r = L;
	while(l < r) {
		const int mid = l + (r - l >> 1);
		if(check(mid)) //满足条件,说明mid小了,调大一点
			l = mid + 1;
		else    //不满足条件,说明mid大了,调小一点
			r = mid - 1;
 	}
	if(!check(l)) l -= 1;
	cout << l;
    return 0;
}

31.实数二分模板

#include <bits/stdc++.h>
using namespace std;
/*
    实数二分不用考虑整数取整问题
    
    实数二分模板:
*/
const double eps = 1e-7;

int main() {
	while(r - l > eps) {
		double mid = l + (r - l >> 1);
		if(check(mid)) r = mid;
		else    l = mid;
	}
    return 0;
}

32.猴子吃桃 递归

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <fstream>
#include <map>
#include <stack>
#include <ctime>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll unsigned long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 1e3+10;
int n = 2;
int dfs(int k) {
	if(n == 1) return k;
	else {
		k = (k + 1) * 2;
		n--;
		dfs(k);
	}
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
	cout << dfs(1);
    return 0;
}

33.dfs实现组合枚举

www.acwing.com/problem/con…

image.png

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <fstream>
#include <map>
#include <stack>
#include <ctime>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll unsigned long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 1e3+10;
int n = 3;
int m = 2;
int res = 0;
int vis[N] = {0};
int a[N];
void dfs(int k, int index) {
	if(k + n - index < m) return;
	if(k == m + 1) {
		for(int i = 1; i <= m; ++i)
			cout << a[i] << " ";
		cout << endl;
		return;
	}
	for(int i = index; i <= n; ++i) {
		a[k] = i;
		dfs(k+1, i+1);
		a[k] = 0;
	}
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    dfs(1,1);
    return 0;
}

34.赶鸭子 递归

image.png

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <fstream>
#include <map>
#include <stack>
#include <ctime>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll unsigned long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 1e3+10;
int dfs(int k) {
	int res;
	if(k == 8) {
		return 2;
	} else {
		res = (dfs(k+1)+1)*2;
		return res;
	}
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << dfs(1);
    return 0;
}

35.求数位之和 递归

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <fstream>
#include <map>
#include <stack>
#include <ctime>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll unsigned long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 1e3+10;
int dfs(int n) {
	if(n < 10) {
		return n;
	} else {
		return (n % 10) + dfs(n / 10);
	}
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int n;
    cin >> n;
	cout << dfs(n);
    return 0;
}

36.汉诺塔

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <fstream>
#include <map>
#include <stack>
#include <ctime>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll unsigned long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 1e3+10;
void Hanoi(int n, char src, char mid, char dest) {
	//将src座上的n个盘子,以mid作为中转,移动到dest座
	if(n == 1) { //只需移动一个盘子
	//直接将盘子从src移动到dest即可
		cout << src << "->" << dest << endl;
		return; //递归终止
	}
	Hanoi(n-1,src,dest,mid); //先将n-1个盘子从src移动到mid
	cout << src << "->" << dest << endl;
	//再将一个盘子从src移动到dest
	Hanoi(n-1,mid,src,dest); //最后将n-1个盘子从mid移动到dest
	return;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int n;
    cin >> n;
    Hanoi(n,'A','B','C');
    return 0;
}

37.方格分割

www.lanqiao.cn/problems/64…

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <fstream>
#include <map>
#include <stack>
#include <ctime>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll unsigned long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 10;
int n = 6;
bool vis[N][N];
int res = 0;
int dx[]= {-1,1,0,0}, dy[] = {0,0,-1,1};
void dfs(int x, int y) {
	//base case
    if(x == 0 || y == 0 || x == n || y == n) {
    	res++;
    	return;
	}
	for(int i = 0; i < 4; ++i) { //上下左右四个方向
		x += dx[i]; y += dy[i]; //走一步
		if(!vis[x][y]) {
			vis[x][y] = true;
			vis[n - x][n - y] = true;
			dfs(x, y); //继续深搜
			//回溯
			vis[n - x][n - y] = false;
			vis[x][y] = false;
		}
		x -= dx[i]; y -= dy[i]; //回溯
	}
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
	vis[3][3] = true; //中心点
	dfs(3,3);
	cout << res / 4 << endl;
    return 0;
}

38.四平方和

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <fstream>
#include <map>
#include <stack>
#include <ctime>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll unsigned long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 1e6+10;
int a[N];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
	int n;
	cin >> n;
	for(int a = 0; a <= sqrt(n); ++a) {
		for(int b = a; a*a+b*b <= n; ++b) {
			for(int c = b; a*a+b*b+c*c <= n; ++c) {
				int t = n - a*a - b*b - c*c;
				int d = sqrt(t);
				if(d*d == t && d >= c) {
					printf("%d %d %d %d\n",a,b,c,d);
					return 0;
				}
			}
		}
	}
    return 0;
}

39.区间最大值

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <fstream>
#include <map>
#include <stack>
#include <vector>
#include <ctime>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll unsigned long long
#define INF 0x3f3f3f3f
#define F(i,a,b) for(int i = a; i < b; ++i)
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 1e5+10;
int n, m;
int a[N], dp_max[N][40];
void st_init() {
	for(int i = 1; i <= n; ++i) //初始化区间长度为1时的值
		dp_max[i][0] = a[i]; //最大值,如果题目要求区间最小值就改为dp_min
	int p = log2(n);
	for(int k = 1; k <= p; ++k) //倍增计算小区间,小区间=>大区间
		for(int s = 1; s +(1 << k) <= n + 1; ++s)
			dp_max[s][k] = max(dp_max[s][k-1], dp_max[s+(1<<(k-1))][k-1]);
}
int st_query(int l, int r) {
	int k = log2(r - l + 1);
	return max(dp_max[l][k], dp_max[r-(1<<k)+1][k]);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
	cin >> n >> m;
	for(int i = 1; i <= n; ++i) cin >> a[i];
	st_init();
	//查询m次
	for(int i = 1; i <= m; ++i) {
		int l, r;
		cin >> l >> r; //左右区间
		cout << st_query(l,r) << endl;
	}
	return 0;
}

40. 埃及分数

www.lanqiao.cn/problems/72…

#include <iostream>
#include <algorithm>
#include <cmath>
#include <string>
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#pragma GCC optimize(2)
using namespace std;
/*
   imp: 分数,将一个分数分解为:1/a + 1/b的形式
   未知:那么,2/45一共有多少个不同的埃及分解(满足加法交换律的算同一种分解)
   已知:分数里的a和b必须是不同的2个整数,分子必须为1
    2/15一共有4种不同的分解法
		1. 1/8 + 1/120
		2. 1/9 + 1/45
		3. 1/10 + 1/30
		4. 1/12 + 1/20
   思路:
	 1.定义变量res=0,N=10000
	 2.for i从1开始枚举,to 45,do:
		a. for j从N开始枚举,to 45,do:
			a-1. 如果45*(i+j) == 2*i*j,就
				I.res+1
				II. N = j,排除完当j值时会满足
				III. 退出循环
			a-2.否则如果45*(i+j) > 2*i*j, 退出循环(因为分数已经比1大,不可能)
	3. 输出res
*/

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int res = 0;
	int N = 10000;
	for(int i = 1; i <= 15; ++i) {
		for(int j = N; j > 15; --j) {
			if(15*(i+j) == 2*i*j) {
					res++;
					N = j;
					cout << i << "/" << j << endl;
					break;
			} else if(15*(i+j) == 2*i*j) break;
		}
	}
	cout << res;
	return 0;
}

41.01背包递归型写法

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#pragma GCC optimize(2)
using namespace std;
/*
	imp:N件物品有一个容量时m的背包,每件物品只能使用一次
	未知:求哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
	已知:第i件物品的重量是wi,价值是vi
	输入:n,m; n行wi和vi
	输出:一个整数,表示最大价值
	
	思路:
		1. 定义变量n,m,数组w,数组v,二维数组memo
		2. 输入n,m; 然后循环n次输入wi和vi
		3. memo表初始化为-1
		4. def int dfs(i, j) //i表示物品编号,j表示物品重量
			a.base case: 如果j <= 0,就返回0
			b.如果没得选了,i > n, 就返回0
			c.如果memo表不等于-1,就返回memo表
			d. 定义变量no_choose为dfs(i+1,j)
			e. 如果j大于等于w[i]
				d-1.就定义变量choose为 当前选择的物品价值 + dfs(i+1, j-w[i])
				d-2. 那么memo表为max(choose,no_choose)
				d-3. 返回memo表
			f. 如果j小于w[i], 就将no_choose赋值给memo表,并返回memo表
*/
const int N = 1010;
int n, m;
int w[N], v[N], memo[N][N];

int dfs(int i, int j) { //i:product id, j: product weight(high~low)
	//base case
	if(i == n) return 0;
	if(j <= 0) return 0;
	//计算子问题时先查表
	if(memo[i][j] != -1) return memo[i][j];
	//不选当前物品
	int no_choose = dfs(i+1,j);
	//背包容量能装得下当前物品
	if(j >= w[i]) {
		int choose = v[i] + dfs(i+1,j-w[i]);
		//比较
		memo[i][j] = max(choose,no_choose);
	} else {
		//装不下
		memo[i][j] = no_choose;
	}
	return memo[i][j];
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin >> n >> m;
	for(int i = 0; i < n; ++i) cin >> w[i] >> v[i];
	memset(memo,-1,sizeof(memo));
	cout << dfs(0,m);
	return 0;
}

42. 钢条切割

1.递归

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#pragma GCC optimize(2)
using namespace std;
/*
注意:如果长度为 [公式] 英尺的钢条的价格 [公式] 足够大,那么最优解就是不需要切割。
   imp: s公司购买长钢条,将其切割为短钢条出售
   未知:求最佳的切割方案,使得销售收益 rn 最大
   已知:假设已经知道s公司出售一段长为i英寸的钢条的
   价格为pi(i=1,2...,单位为元)。
   
   长度i:  [1, 2, 3, 4, 5, 6, 7, 8, 9,10]
   价格pi: [1, 5, 8,16,10,17,17,20,24,30]
    
   考虑 n = 4 的情况,那么有以下几种切割方式:

	1.切割为四段,长度为:1,1,1,1;总共卖4*1=4元。

	2.切割为三段,长度为:1,1,2;总共卖2*1+1*5=7元。

	3.切割为两段,长度为:1,3;总共卖1*1+1*8=9元。

	4.切割为两段,长度为:2,2;总共卖2*5=10元。

	5.不切割,长度为:4;总共卖1*9=9元。
    	那么从以上方案找到最佳的方案:即10元
    
   思路:
		1, 定义变量:一维数组memo,res = 0,一维数组p(价格)
		1-1. 输入n(钢管的长度)
		2. def int dfs(x) //x表示钢条的长度
			a.如果钢条的长度x等于0,返回0
			b.for 钢管长度i从1开始枚举,到x结束,do:
				b-1.如果memo[x-i]等于-1
					I.就将dfs(x - i)赋值给memo[x-i]
				b-2.定义变量v为 p[i-1] + memo[x-i]
				b-3. 将v和res中的最大值赋值给res
			c.将res赋值给memo[x]
			d.返回res
		3. 将memo数组初始化为-1
		4. 输出dfs(n)
*/
int memo[100],p[100] = {1,5,8,9,10,17,17,20,24,30};
int  n;
int dfs(int x) { //x表示钢管的长度
	if(x == 0) return 0;
	int res = 0;
	for(int i = 1; i <= x; ++i) {
		if(memo[x-i] == -1)
			memo[x-i] = dfs(x - i);
			
		int v = p[i-1] + memo[x-i];
		res = max(res, v);
	}
	memo[x] = res;
	return res;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin >> n;
	memset(memo,-1,sizeof(memo));
	cout << dfs(n);
	return 0;
}

2.递推

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#pragma GCC optimize(2)
using namespace std;
/*
注意:如果长度为 [公式] 英尺的钢条的价格 [公式] 足够大,那么最优解就是不需要切割。
   imp: s公司购买长钢条,将其切割为短钢条出售
   未知:求最佳的切割方案,使得销售收益 rn 最大
   已知:假设已经知道s公司出售一段长为i英寸的钢条的
   价格为pi(i=1,2...,单位为元)。
   
   长度i:  [1, 2, 3, 4, 5, 6, 7, 8, 9,10]
   价格pi: [1, 5, 8,16,10,17,17,20,24,30]
    
   考虑 n = 4 的情况,那么有以下几种切割方式:

	1.切割为四段,长度为:1,1,1,1;总共卖4*1=4元。

	2.切割为三段,长度为:1,1,2;总共卖2*1+1*5=7元。

	3.切割为两段,长度为:1,3;总共卖1*1+1*8=9元。

	4.切割为两段,长度为:2,2;总共卖2*5=10元。

	5.不切割,长度为:4;总共卖1*9=9元。
    	那么从以上方案找到最佳的方案:即10元
    
   思路:
		1, 定义变量:一维数组dp,res = 0,一维数组p(价格)
		2. 输入n
		3. dp[0]初始化为0
		4. for 钢管长度i从1开始枚举,to n, do:
			a.for 保留整段j从1开始枚举, to i, do:
				a-1.dp[i]为max(p[j-1] + dp[i - j], dp[i])
				    其实就是从不切割的价格与切割的价格中比较,找到最优值
		5. 输出dp[n]
*/
int dp[100],p[100] = {1,5,8,9,10,17,17,20,24,30};
int  n;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin >> n;
	dp[0] = 0;
	for(int i = 1; i <= n; ++i) {
		for(int j = 1; j <= i; ++j) {
			dp[i] = max(p[j-1]+dp[i-j], dp[i]);
		}
	}
	cout << dp[n];
	return 0;
}

43.最少硬币问题dp

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <sstream>
#include <set>
#include <unordered_set>
#include <string>
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#pragma GCC optimize(2)
#define INF 0x3f3f3f3f
using namespace std;
const int N = 1100;
/*
    imp: 现有若干枚硬币,硬币面值为
    [11,1,5]
    要凑出价值W,至少需要多少枚硬币,例如W=15

    本题贪心不靠谱,因为有反例
    1.贪心思维:面临鼠目寸光的境地
	2.暴力枚举:复杂度太高
	3.dp
	    当我们要凑出价值W=15的硬币,将有3种要面临的情况
			1.取1,将面临凑出价值为14的情况
			2.取5,将面临凑出价值为10的情况
			3.取11,将面临凑出价值为4的情况
        记凑出价值n所需要的最少硬币为memo[n]
	
	未知:
    已知:
    
   dp三要素:用于多阶段决策最优化问题
	1. 阶段->状态->决策
		01背包中:
            阶段:选第i个物品时
            状态: memo[i][j]
            决策:memo[i][j] = max( , )
	2. 两个条件
        最优子结构
        无后效性:i->j->k(现状态不会影响历史状态)
	3. 是否可以用dp
		1.模型匹配:掌握经典模型
		  a.一维:上升子序列,背包模型
		  b.二维:最长公共子序列
		2.寻找规律:规模由小到大,或由大到小,做逐步分析
    4.一般过程
		1.找到过程演变中变化的量(状态),以及变化的规律(状态转移方程)
		2.确定一些初始状态,通常需要用memo数组来保存
		3.利用状态转移方程,解出最终答案
	5.解法:自顶向下(递归,如果有大量重叠子问题,就要带备忘录),自底向上(递推)
    思路:
		1.定义变量amount(总计),数组coins,数组memo
		2.初始化memo表为无穷大
		3.初始化memo[0] = 0
		4.for i=0 to n-1, do: 遍历物品
			a.for j = coins[i] to amount, do: 遍历背包容量
                a-1.如果memo[背包容量j减去币值coins[i]]不等于无穷大
					memo[j] = min(memo[j], memo[j-coins[i]]+1)
		5.如果memo[amount]等于无穷大,就输出负1,否则,输出自身
*/
int memo[N];
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
	int coins[] = {1,2,5};
	int amount = 11;
	int n = sizeof(coins) / sizeof(int);
	memset(memo,0x3f,sizeof(memo));
	memo[0] = 0;
	for(int i = 0; i < n; ++i) {
		for(int j = 1; j <= amount; ++j) {
			memo[j] = min(memo[j], memo[j-coins[i]]+1);
//			cout << memo[j] << " ";
		}
//		cout << endl;
	}
	cout << memo[amount];
    return 0;
}

44.最少硬币问题路径

#include <iostream>
#include <cstring>
#define ll long long
#pragma GCC optimize(2)
#define INF 0x3f3f3f3f
using namespace std;
const int N = 5;
const int M = 250; //定义最大金额,提前打表,用于多组数据测试
int coins[N] = {1,5,10,25,50};
int memo[M]; //memo[i]表示金额i需要的最少硬币
int path[M]={0}; //记录最小硬币的路径

void solve() {
	/*
	//memo表初始化为无穷大
	memset(memo,0x3f,sizeof(memo));
	memo[0] = 0; //金额0所需要的最少硬币为0
	for(int i = 0; i < N; ++i) { //5个阶段
		for(int j = coins[i]; j <= M; ++j) { //每个状态,提前打表
			memo[j] = min(memo[j], memo[j - coins[i]] + 1);
		}
	}
	*/
	//memo表初始化为无穷大
	memset(memo,0x3f,sizeof(memo));
	memo[0] = 0; //金额0所需要的最少硬币为0
	for(int i = 0; i < N; ++i) { //5个阶段
		for(int j = coins[i]; j <= M; ++j) { //每个状态,提前打表
//			memo[j] = min(memo[j], memo[j - coins[i]] + 1);
			if(memo[j] > memo[j - coins[i]] + 1) {
				path[j] = coins[i]; //在每个金额上记录路径
				memo[j] = memo[j - coins[i]] + 1; //递推式
			}
		}
	}
}
void print_path(int* p, int n) { //打印金额n最少的硬币组合
	while(path[n] != 0 && n > 0) {
		cout << path[n] << " ";
		n -= path[n];
	}
	cout << endl;
}
int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0); cout.tie(0);
  int n;
  solve();
  while(cin >> n) {
  	if(memo[n] == INF)
		cout << "WA" << endl;
	else {
		cout << memo[n] << endl; //输出最少硬币个数
		print_path(path, n);
	}
  }
  return 0;
}

45.01背包交替滚动+不要带初始化

#include <bits/stdc++.h>
using namespace std;
const int N = 8000;
int memo[2][N], w[N],v[N];
int n, m;

int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; ++i) cin >> w[i] >> v[i];
    int now = 0, old = 1;
    //阶段=>状态+背包容量
    for(int i = 1; i <= n; ++i) {
    	swap(old,now);
        for(int j = 1; j <= m; ++j) {
            if(j >= w[i]) {
                memo[now][j] = max(memo[old][j], memo[old][j-w[i]]+v[i]);
            } else {
                memo[now][j] = memo[old][j];
            }
        }
    }
    cout << memo[now][m];
    return 0;
}

46.字符串转换 线性dp

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3*3;
string s, t;
int memo[N][N];
/*
	1.删除一个字符
	2.插入一个字符
	3.将一个字符改为另一个字符
	问最少需要操作多少次才可以使字符串s转换为
	字符串t
	n:指的是长度为n的a数组存储在[a1,an]
	m:指的是长度为m的b数组存储在[b1,bm]
	memo[n][m]:a数组的前i个字符转换为b数组的最少操作次数
        
         设方程 memo表
          | 0 1 2 3 4
              a b c f
        0 | 0 1 2 3 4      memo[1][1] =1 memo[1][2] = 1
    b   1 | 1 1 1 2 3
    c   2 | 2
    f   3 | 3
    e   4 | 4
        
	思路:
	    初始化要注意一下
        如果s[i] == t[j],memo[i][j] = memo[i-1][j-1]
        否则,就从三个方向(正上方,左侧,左上角)找出最小的那一项再加1赋值给memo[i][j]
        
*/
int main() {
	cin >> s >> t;
	int len1 = s.size();
	int len2 = t.size();
	//如果没有公共子序列,就初始化为i
	for(int i = 0; i <= len1; ++i)
		memo[i][0] = i;
	//如果没有公共子序列,就初始化为j
	for(int j = 0; j <= len2; ++j)
		memo[0][j] = j;
	for(int i = 1; i <= len1; ++i) {
		for(int j = 1; j <= len2; ++j) {
			if(s[i-1] == t[j-1]) {
				memo[i][j] = memo[i-1][j-1]; //原样
			} else {
				memo[i][j] = min(min(memo[i-1][j],memo[i][j-1]),memo[i-1][j-1])+1;
			}
		}
	}
	cout << memo[len1][len2];
    return 0;
}

47.装箱问题

www.lanqiao.cn/problems/76…

#include <iostream>
using namespace std;
int memo[20010];
int w[40];
int main() {
    int n, m;
    scanf("%d%d",&m,&n);
    for(int i = 1; i <= n; ++i) scanf("%d",&w[i]);
    for(int i = 1; i <= n; ++i) {
        for(int j = m; j >= w[i]; --j) {
			if(memo[j] < memo[j-w[i]] + w[i] )
                memo[j] = memo[j-w[i]]+w[i];
        }
    }
    printf("%d\n",m-memo[m]);
    return 0;
}

bitset用法

#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <stack>
#include <cstring>
#include <queue>
#include <sstream>
#include <bitset>
#define ull unsigned long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a / gcd(a,b) * b
#define ll long long
#define INF 0x3f3f3f3f
const double PI = acos(-1.0);
using namespace std;
const unsigned int M = 1e9 + 7;
const int N = 1e5+7;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n;
	while(cin >> n) {
		bitset<32> bt(n);
//		cout << bt << endl;
		string s = bt.to_string();
		int pos = -1;
		for(int i = 0; i < s.size(); ++i) {
			if(s[i] == '1') {
				pos = i;
				break;
			}
		}
		s = s.substr(pos, s.size()-1);
		cout << s << endl;
	}
	return 0;
}