C++日常刷题积累
今日刷题汇总 - day022
1、添加字符
1.1、题目
1.2、思路
读完题,知道有两个字符串A,B,其中B串的起始长度大于等于A串,然后可以将对A串前或后执行插入操作,使其与B达成一样的长度,且让A,B串中不同字符的个数最少。那么,采用蛮力法枚举所有可能,首先得明白,插入字符课以插入任意字符,所以插入的字符肯定为了与B串相同的字符,那么存在不同的字符只可能是在A串的初始串中,那么又因为可以前插或后插,为了初始串的不同字符个数最少那么,就与A串匹配,让其在最合适的位置满足对应每一位的字符相同,不同字符最少即可。简单画个图直观的理解:
那么接下来,就是程序实现。
1.3、程序实现
首先,按照题目要求输入初始A,B串,并求的A,B串的长度m,n,然后根据思路分析的要找最合适B串的起始位置,使其满足对应位置匹配字符相同的最多,相反就得到最少不同字符的个数了。利用for依次枚举,其中枚举到后面字符串长度小于A串长度就不同枚举了,因为小于长度了就不会匹配了,然后,内循环就是执行匹配字符,枚举到B串 i 位置为起始位置上时,统计A,B不同字符的个数,保存到ret中,并随着枚举不断更新即可,最后遍历完输出就得到最少不同字符个数了。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string A, B;
cin >> A >> B;
int m = A.size();
int n = B.size();
int ret = m;
// 枚举 B串最合适 的起始位置
for (int i = 0; i <= n - m; i++)
{
int tmp = 0;
//匹配A串 i 起始位置时,A,B串的不同字符情况
for (int j = 0; j < m; j++)
{
if (A[j] != B[i + j])
{
tmp++;
}
}
ret = min(tmp, ret);
}
cout << ret << endl;
return 0;
}
2、数组变换
2.1、题目
2.2、思路
读完题,知道n个元素的数组,可对数组中每个元素执行2操作,判断是否能够让数组中每一个元素都最终相等。其中,这个操作的使用次数不限,也可以不使用,并且可以对同一个位置使用多次。最终成功的就输出"Yes",不能完成的就输出"No"即可。那么,根据题目和示例分析,该数组中的元素会存在2的n次方关系,因为2,3不能通过一直2得到相同的数值的,也就是说,需要判断较小的数能否变换成最大值,如果能够变换成功,那么最⼤的数除以剩下的数的商,⼀定都是 2 的 n 次⽅。以1,2 ,4,8,16为例,满足所有元素都能通过2达成全部相等,同时验证了最大数除以剩余元素的商属于2的n次方,再以3,6,12,24为例,这组元素本身并不是属于2的n次方,那么也是可以通过2得到统一的相等数值的,发现通常以最大值为基准的,其次,验证了最大数除以剩余元素的商属于2的n次方,同时发现,需要较大值能整除较小值。所以综上所述,需要具备以下条件:
(1)、较大值除以较小值满足商属于2的n次方;
(2)、较大值能够整除较小值。
那么问题就演变为,怎么判断商是否属于2的n次方呢?
1.蛮力法,让这个数不断地除以2,直到最后等于1为止,说明是属于2的n次方的,否则不满足;
2.位运算,利用二进制的特点2的n次方满足以下两个情况:
(1)、方法1:x - x&(-x) = 0,则满足
(2)、方法2:x & (x-1) = 0,则满足。
那么接下来,就是程序实现。
2.3、程序实现
首先,按照题目要求写好输入,并求得数组中最大值作为基准值,然后再次遍历数组,结合标志位,判定是否符合思路分析中的两个条件即可。
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int n;
cin >> n;
int arr[51];
int maxval = 0;
for(int i = 0;i< n;i++)
{
cin >> arr[i];
maxval = max(maxval,arr[i]);
}
int flag = 0;
for(int i = 0;i < n;i++)
{
if(maxval % arr[i] != 0)
{
flag = 1;
break;
}
int x = maxval / arr[i];
if(x & (x-1) != 0)
{
flag = 1;
break;
}
}
if(flag)
cout << "NO" << endl;
else
cout << "YES" << endl;
return 0;
}
3、装箱问题
3.1、题目
3.2、思路
读完题,知道在限制了容量为V的箱子中,对于n个物品,任取若干个物品后要求尽可能让剩余空间最少即可。与之前写过的0/1背包问题类似,都是属于贪心加dp动态规划问题,因为存在放与不放的选择,限制了容量为V的上限,所以采用dp动态规划问题,那么就确定状态表示和状态转移方程,以dp[i][j]:表示在前 i 个物品中挑选,总体积不超过 j 的条件下,此时的最大体积是多少。那么剩余容量就是V - dp[i][j]。继续,推导状态转移方程,首先,对于第 i 个物品时可选择放入或不放入,所以存在两种情况:
(1)、对于选择放入第 i 个物品时,对于前面 i-1个物品时满足最大体积就是dp[i-1],然后总体积不超过 j ,那么对于 i -1个物品时就不能超过 j 减去第 i 个物品的体积 v[i] ,最后再加上第 i 个物品的体积,即,dp[i][j] = dp[i-1][j - v[i]] + v[i];值得注意的是,当选择放入第 i 个物品的前提条件是前 i -1 个物品能放下,即物品空间 j - v[i] 满足大于等于v[i]。
(2)、那么当选择不放入第 i 个物品时,在前面的物品满足最大体积就是到 i-1物品时满足,且也需要 i-1 个物品时总体积不能超过 j ,即dp[i][j] = dp[i-1][j]即可。
所以综上所述:
(1)、状态表示dp[i][j]:表示在前 i 个物品中挑选,总体积不超过 j 的条件下,此时的最大体积是多少。那么剩余容量就是V - dp[i][j]。
(2)、状态转移方程dp[i][j]两种情况:
a、选择不放入第 i 个物品时:dp[i][j] = dp[i-1][j];
b、选择放入第 i 个物品时:j - arr[i] >= 0 ,dp[i][j] = max(dp[i-1][j - v[i]] + v[i],dp[i][j]);
(3)、初始化依然采用方便的下标从1开始计数,所以多增加一行一列的方案即可。
那么接下来,就是程序实现。
3.3、程序实现 – dp(0/1背包问题)
(1)、状态表示dp[i][j]:表示在前 i 个物品中挑选,总体积不超过 j 的条件下,此时的最大体积是多少。那么剩余容量就是V - dp[i][j]。
(2)、状态转移方程dp[i][j]两种情况:
a、选择不放入第 i 个物品时:dp[i][j] = dp[i-1][j];
b、选择放入第 i 个物品时:j - arr[i] >= 0 ,dp[i][j] = max(dp[i-1][j - v[i]] + v[i],dp[i][j]);
(3)、初始化依然采用方便的下标从1开始计数,所以多增加一行一列的方案即可。
#include <iostream>
using namespace std;
const int N = 31;
const int M = 2e4 + 10;
int arr[N];
int dp[N][M];
int main()
{
int v,n;
cin >> v >> n;
for(int i = 1;i <= n;i++)
cin >> arr[i];
for(int i = 1;i <= n;i++)
{
for(int j = 0;j <= v;j++)
{
dp[i][j] = dp[i-1][j];
if(j >= arr[i])
dp[i][j] = max(dp[i][j],dp[i-1][j-arr[i]] + arr[i]);
}
}
cout << v - dp[n][v] << endl;
return 0;
}