1. 地宫取宝 *
#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.最长上升子序列长度
时间复杂度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.最长公共子序列
#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 *
#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. 矩阵旋转
#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.扫地机器人
贪心+二分
#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. 星期一
#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. 分数
#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,然后输出
#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.阶乘计算
#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.拼数
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.统计数字
- 使用桶排序的思想,开一个和数字大小一致的数组;但是数字的范围是10^9,因此这里是不可行的。
- 使用map,key对应着数字,value表示出现频率,最后用迭代器遍历一遍即可。
- 书写二叉排序树,节点记录值得同时记录下出现的频率;最后进行一遍中序遍历
方法一: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.付账问题
#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.日志统计
#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. 二分模板
#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实现组合枚举
#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.赶鸭子 递归
#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.方格分割
#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. 埃及分数
#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.装箱问题
#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;
}