#include<iostream>usingnamespace std;intmain(){int n, m;
cin >> n >> m;double ret =1.0;for(int i =0; i < m; i++){
ret *=0.8;}for(int i =0; i < n - m; i++){
ret *=0.2;}for(int i = n; i >= n - m +1; i--){
ret *= i;}for(int i = m; i >=2; i--){
ret /= i;}printf("%.4lf", ret);return0;}
1.2 ruby和薯条(排序 + 二分 / 双指针)
题目链接: ruby和薯条
题目描述:
解法:
算法思路:
解法⼀:排序 + 二分。 先排序,然后枚举较大值,在 [1, i - 1] 区间找差值的左右端点即可。
// 解法⼀:排序 + ⼆分#include<iostream>#include<algorithm>usingnamespace std;constint N =2e5+10;intmain(){int n, l, r;
cin >> n >> l >> r;int arr[N];for(int i =1; i <= n; i++){
cin >> arr[i];}sort(arr +1, arr + n +1);longlong ret =0;for(int i =2; i <= n; i++){int L, R;// 找左端点int left =1;int right = i -1;while(left < right){int mid =(right + left)/2;if(arr[mid]>= arr[i]- r){
right = mid;}else{
left = mid +1;}}if(arr[left]>= arr[i]- r){
L = left;}else{
L = left +1;}// 找右端点
left =1;
right = i -1;while(left < right){int mid =(right + left +1)/2;if(arr[mid]<= arr[i]- l){
left = mid;}else{
right = mid -1;}}if(arr[left]<= arr[i]- l){
R = left;}else{
R = left -1;}if(R >= L){
ret += R - L +1;}}
cout << ret << endl;return0;}// 解法⼆:排序 + 前缀和 + 滑动窗⼝#include<iostream>#include<algorithm>usingnamespace std;constint N =2e5+10;int n, l, r;int arr[N];// 找出差值在 [0, x] 之间⼀共有多少对longlongfind(int x){int left =0, right =0;longlong ret =0;while(right < n){while(arr[right]- arr[left]> x){
left++;}
ret += right - left;
right++;}return ret;}intmain(){
cin >> n >> l >> r;for(int i =0; i < n; i++) cin >> arr[i];sort(arr, arr + n);
cout <<find(r)-find(l -1)<< endl;return0;}
#include<iostream>usingnamespace std;constint MOD =1e9+7;intmain(){int n =0;
cin >> n;int x =1;int y =2;for(int i =2; i <= n; i++){int xx = x, yy = y;
x =(2* yy +1)% MOD;
y =((2* yy)% MOD +2+ xx)% MOD;}
cout << x <<" "<< y << endl;return0;}
2. Day44
2.1 差值(排序)
题目链接: 最小差值
题目描述:
解法:
算法思路:排序,然后计算相邻两个数之差的最小值即可。
C++ 算法代码:
classSolution{public:intminDifference(vector<int>& a){sort(a.begin(), a.end());longlong ret =1e16+10;;for(int i =1; i < a.size(); i++){
ret =min(ret,(longlong)a[i]- a[i -1]);}return ret;}};
2.2 kotori和素因子(DFS)
题目链接: kotori和素因子
题目描述:
解法:
算法思路:递归型枚举所有的情况。
C++ 算法代码:
#include<iostream>#include<cmath>usingnamespace std;constint N =15, M =1010;int n =0;int arr[N]={0};bool use[M];// 记录路径中⽤了哪些值int path;// 记录当前路径中所有元素的和int ret =0x3f3f3f3f;// 统计最终结果boolisPrim(int x){if(x <=1){returnfalse;}for(int i =2; i <=sqrt(x); i++){if(x % i ==0){returnfalse;}}returntrue;}voiddfs(int pos){if(pos == n){
ret =min(ret, path);}for(int i =2; i <= arr[pos]; i++){if(arr[pos]% i ==0&&isPrim(i)&&!use[i]){
path += i;
use[i]=true;dfs(pos +1);// 回溯 - 恢复现场
path -= i;
use[i]=false;}}}intmain(){
cin >> n;for(int i =0; i < n; i++){
cin >> arr[i];}dfs(0);if(ret ==0x3f3f3f3f){
cout <<-1<< endl;}else{
cout << ret << endl;}return0;}
2.3 dd爱科学1.0(最长上升子序列 - 贪心 + 二分)
题目链接: dd爱科学1.0
题目描述:
解法:
算法思路:
要想改动最小,就应该在最长非下降子序列的基础上,对不是最长的部分进行更换。
因为这道题的数据范围比较大,所以应该用贪心 + 二分求出最长非下降子序列的长度。
C++ 算法代码:
#include<iostream>usingnamespace std;constint N =1e6+10;intmain(){int n =0;
string s;
cin >> n >> s;char dp[N];// dp[i] 表⽰:⻓度为 i 的所有的⼦序列中,最⼩的末尾是多少int ret =0;for(int i =0; i < n; i++){char ch = s[i];if(ret ==0|| dp[ret]<= ch){
dp[++ret]= ch;}else{// ⼆分出 ch 应该放的位置int left =1;int right = ret;while(left < right){int mid =(left + right)/2;if(dp[mid]> ch){
right = mid;}else{
left = mid +1;}}
dp[left]= ch;}}
cout << n - ret << endl;return0;}
3. Day45
3.1 kanan和高音(模拟 + 双指针)
题目链接: kanan和高音
题目描述:
解法:
算法思路:从前往后遍历,用双指针找出⼀段能唱完的区域,然后更新指针继续找下⼀段。
C++ 算法代码:
#include<iostream>usingnamespace std;constint N =2e5+10;int n;int arr[N];intmain(){
cin >> n;for(int i =0; i < n; i++){
cin >> arr[i];}int ret =1;int left =0;while(left < n){int right = left;while(right +1< n && arr[right +1]- arr[right]<=8){
right++;}
ret =max(ret, right - left +1);
left = right +1;}
cout << ret << endl;return0;}
3.2 拜访(BFS)
题目链接: MT3 拜访
题目描述:
解法:
算法思路:在层序遍历的过程中,维护额外的信息。
C++ 算法代码:
classSolution{public:int x1, y1, x2, y2;int dist[15][15]={0};// 判断是否经过int cnt[15][15]={0};// 到改位置的最短路径的数量int dx[4]={0,0,1,-1};int dy[4]={1,-1,0,0};intbfs(vector<vector<int>>& CityMap,int n,int m){memset(dist,-1,sizeof dist);
queue<pair<int,int>> q;
q.push({x1, y1});
dist[x1][y1]=0;
cnt[x1][y1]=1;while(q.size()){auto[a, b]= q.front();
q.pop();for(int i =0; i <4; i++){int x = a + dx[i];int y = b + dy[i];if(x >=0&& x < n && y >=0&& y < m && CityMap[x][y]!=-1){if(dist[x][y]==-1)// 第⼀次到这个位置{
dist[x][y]= dist[a][b]+1;
cnt[x][y]+= cnt[a][b];
q.push({x, y});}else{if(dist[a][b]+1== dist[x][y])// 是不是最短路{
cnt[x][y]+= cnt[a][b];}}}}}return cnt[x2][y2];}intcountPath(vector<vector<int>>& CityMap,int n,int m){for(int i =0; i < n; i++){for(int j =0; j < m; j++){if(CityMap[i][j]==1){
x1 = i;
y1 = j;}elseif(CityMap[i][j]==2){
x2 = i;
y2 = j;}}}returnbfs(CityMap, n, m);}};
3.3 买卖股票的最好时机(四)(动态规划)
题目链接: DP33 买卖股票的最好时机(四)
题目描述:
解法以及算法思路:
状态表示:为了更加清晰的区分「买入」和「卖出」,我们换成「有股票」和「无股票」两个状态。
f[i][j] 表示:第 i 天结束后,完成了 j 笔交易,此时处于「有股票」状态的最大收益;
g[i][j] 表示:第 i 天结束后,完成了 j 笔交易,此时处于「无股票」状态的最大收益。
状态转移方程:
对于 f [i][j] ,我们也有两种情况能在第 i 天结束之后,完成 j 笔交易,此时手里「有股票」的状态:
在 i - 1 天的时候,手里「有股票」,并且交易了 j 次。在第 i 天的时候,啥也不干。此时的收益为 f[i - 1][j] ;
在 i - 1 天的时候,手里「没有股票」,并且交易了 j 次。在第 i 天的时候,买了股票。那么 i 天结束之后,我们就有股票了。此时的收益为 g[i - 1][j] - prices[i] ;
我们的交易次数是不会超过整个天数的⼀半的,因此我们可以先把 k 处理⼀下,优化⼀下问题的规模:k = min(k, n / 2)。
如果画一个图的话,它们之间交易关系如下:
C++ 算法代码:
#include<iostream>usingnamespace std;constint N =1010, M =110;int n, k, p[N];int f[N][M], g[N][M];intmain(){
cin >> n >> k;for(int i =0; i < n; i++){
cin >> p[i];}
k =min(k, n /2);for(int j =0; j <= k; j++){
f[0][j]= g[0][j]=-0x3f3f3f3f;}
f[0][0]=-p[0];
g[0][0]=0;for(int i =1; i < n; i++){for(int j =0; j <= k; j++){
f[i][j]=max(f[i -1][j], g[i -1][j]- p[i]);
g[i][j]= g[i -1][j];if(j >=1){
g[i][j]=max(g[i][j], f[i -1][j -1]+ p[i]);}}}int ret =0;for(int j =0; j <= k; j++){
ret =max(ret, g[n -1][j]);}
cout << ret << endl;return0;}
4. Day46
4.1 AOE还是单体?(贪心)
题目链接: AOE还是单体?
题目描述:
解法:
算法思路。小贪心:
如果使用一次 AOE 造成的伤害比消耗的蓝量多,那就使用;
否则就一直使用单体伤害。
C++ 算法代码:
#include<iostream>#include<algorithm>usingnamespace std;constint N =2e5+10;intmain(){int n, x;
cin >> n >> x;int arr[N];for(int i =1; i <= n; i++){
cin >> arr[i];}sort(arr +1, arr + n +1);longlong ret =0;int index =max(0, n - x);
ret += arr[index]* x;for(int i = index +1; i <= n; i++){
ret += arr[i]- arr[index];}
cout << ret << endl;return0;}
4.2 kotori和n皇后(哈希表)
题目链接: kotori和n皇后
题目描述:
解法:
算法思路:使用哈希表标记行列以及两个对角线。
C++ 算法代码:
#include<iostream>#include<unordered_set>usingnamespace std;int k, t;int ret =1e5+10;// 第⼀次出现互相攻击的皇后的个数
unordered_set<longlong> row;// 标记⾏ y
unordered_set<longlong> col;// 标记列 x
unordered_set<longlong> dig1;// 标记主对⻆线 y - x
unordered_set<longlong> dig2;// 标记副对⻆线 y + xintmain(){
cin >> k;for(int i =1; i <= k; i++){int x, y;
cin >> x >> y;if(ret !=1e5+10){continue;}if(row.count(y)|| col.count(x)|| dig1.count(y - x)|| dig2.count(y + x)){
ret = i;}
row.insert(y);
col.insert(x);
dig1.insert(y - x);
dig2.insert(y + x);}
cin >> t;while(t--){int i =0;
cin >> i;if(i >= ret){
cout <<"Yes"<< endl;}else{
cout <<"No"<< endl;}}return0;}
4.3 取金币(动态规划 - 区间dp)
题目链接: NC393 取金币
题目描述:
解法:
算法思路。区间 dp:
为了方便能处理边界情况,将原数组前后添加⼀个 1,并不影响最后的结果。
状态表示:
dp[i][j] 表示: [i, j] 区间⼀共能获得多少金币。
C++ 算法代码:
classSolution{public:int arr[110]={0};int dp[110][110]={0};intgetCoins(vector<int>& coins){int n = coins.size();
arr[0]= arr[n +1]=1;for(int i =1; i <= n; i++){
arr[i]= coins[i -1];}for(int i = n; i >=1; i--){for(int j = i; j <= n; j++){for(int k = i; k <= j; k++){
dp[i][j]=max(dp[i][j], dp[i][k -1]+ dp[k +1][j]+ arr[i -1]* arr[k]* arr[j +1]);}}}return dp[1][n];}};
5. Day47
5.1 矩阵转置(数学)
题目链接: BC138 矩阵转置
题目描述:
解法:
算法思路:观察转置前和转置后下标的关系即可。
C++ 算法代码:
#include<iostream>usingnamespace std;constint N =15;int n, m;int arr[N][N];intmain(){
cin >> n >> m;for(int i =0; i < n; i++){for(int j =0; j < m; j++){
cin >> arr[i][j];}}for(int j =0; j < m; j++){for(int i =0; i < n; i++){
cout << arr[i][j]<<" ";}
cout << endl;}return0;}
5.2 四个选项(DFS + 剪枝 + 哈希表)
题目链接: 四个选项
题目描述:
解法:
算法思路:用递归枚举出所有的情况,注意剪枝。
C++ 算法代码:
#include<iostream>#include<vector>usingnamespace std;int cnt[5];// ⽤数组存每⼀个选项出现多少次int m, x, y;bool same[13][13];// 存哪些题的答案是相同的int ret;
vector<int> path;// 记录路径⾥⾯选了哪些选项boolisSame(int pos,int cur){for(int i =1; i < pos; i++){if(same[pos][i]&& path[i]!= cur){returnfalse;}}returntrue;}voiddfs(int pos){if(pos >12){
ret++;return;}for(int i =1; i <=4; i++){if(cnt[i]==0)// 没有使⽤次数{continue;}if(!isSame(pos, i))// 需要相同的位置,没有相同{continue;}
cnt[i]--;
path.push_back(i);dfs(pos +1);
path.pop_back();
cnt[i]++;}}intmain(){for(int i =1; i <=4; i++){
cin >> cnt[i];}
cin >> m;while(m--){
cin >> x >> y;
same[x][y]= same[y][x]=true;}
path.push_back(0);// 先放进去⼀个占位符dfs(1);
cout << ret << endl;return0;}
5.3 接雨水问题(双指针)
题目链接: 接雨水
题目描述:
解法:
算法思路:考虑每一根柱子上方雨水的高度。
C++ 算法代码:
classSolution{public:inttrap(vector<int>& height){int n = height.size();
vector<int>left(n);
vector<int>right(n);
left[0]= height[0];for(int i =1; i < n; i++){
left[i]=max(left[i -1], height[i]);}
right[n -1]= height[n -1];for(int i = n -2; i >=0; i--){
right[i]=max(right[i +1], height[i]);}int ret =0;for(int i =1; i < n -1; i++){
ret +=min(left[i], right[i])- height[i];}return ret;}};
6. Day48
6.1 疯狂的自我检索者(贪心)
题目链接: 疯狂的自我检索者
题目描述:
解法:
算法思路:小贪心~
C++ 算法代码:
#include<iostream>usingnamespace std;intmain(){int n, m, a;
cin >> n >> m;int sum =0;for(int i =0; i < n - m; i++){
cin >> a;
sum += a;}printf("%.5lf %.5lf\n",(sum + m)*1.0/ n,(sum + m *5)*1.0/ n);return0;}
#include<iostream>#include<algorithm>usingnamespace std;constint N =1e5+10;longlong n, k;longlong arr[N];longlong sum[N]={0};// 前缀和数组longlongcal(int l,int r){int mid =(l + r)/2;return(mid - l - r + mid)* arr[mid]-(sum[mid -1]- sum[l -1])+(sum[r]- sum[mid]);}intmain(){
cin >> n >> k;for(int i =1; i <= n; i++){
cin >> arr[i];}sort(arr +1, arr + n +1);for(int i =1; i <= n; i++){
sum[i]= sum[i -1]+ arr[i];}int left =1;int right =1;int ret =1;while(right <= n){longlong cost =cal(left, right);while(cost > k){
left++;
cost =cal(left, right);}
ret =max(ret, right - left +1);
right++;}
cout << ret << endl;return0;}