27.游戏英雄升级潜力评估
题解:
这道题目感觉应该放在简单区吧.. 根据题目意思分析可得,选择一个等级最低的英雄跟一个等级不是最低的英雄出来历练,则等级大的那个英雄一定可以到达正无穷级,故本题答案为n-等级最小的英雄个数。 代码:
int solution(int n, std::vector<int> u) {
// write code here
return n - count(u.begin(), u.end(), *min_element(u.begin(), u.end()));
}
时间复杂度O(n)
字典序最小的01串
题解:
本题考查点是贪心+双指针。
首先,对于第 位置上的 1,要把它变为 0 的最小代价就是找到这个 1 右边的第一个 0 (假设在第 位置),然后交换,代价是 。 为什么题面说的是交换相邻位置,但是这里也可以直接交换不相邻的位置的01呢?因为找到的 和 之间的子串一定是 1111...0 的形式,每次交换相邻位置把0换到第一位得到的串是 01111...,而直接交换开头的1和结尾的0,得到的串也是0111...,故两种操作是等价的。 那么可以利用双指针,从左到右遍历遇到的每一个1,找到离他们最近的0,如果当前剩余的操作次数能够交换这一对1和0的位置,则交换,并让操作次数减去这次操作的代价,否则,跳过这个1。继续向后寻找满足条件的可交换的1和0。
string solution(int n, int k, string s) {
// write code here
s=' '+s;
for(int i=1,j=1;i<=n;i++)
{
while(i<=n&&s[i]=='0')i++;
j=max(j,i);
while(j<=n&&s[j]=='1')j++;
if(j>n)break;
if(j-i<=k)
{
swap(s[i],s[j]);
k-=j-i;
}
}
return s.substr(1);
}
时间复杂度O(n).
33.卡牌翻面求和问题
题解:
本题考虑动态规划。要使得最终向上的数字之和能被3整除,也就是说最后的和对3取余为0。 设 表示前 个 数字,最终之和对3取余之后为 的方案数。那么 。 当遍历到第 个位置时,当前位置的数字可以选择 或 ,设 对3取模的结果为 va, 对3取模的结果为vb,则转移方程为 dp[i][(j+va)%3] += dp[i-1][j] , dp[i][(j+vb)%3] += dp[i-1][j],最后答案为dp[n][0]。由于第i个位置的状态只与第i-1位置的状态有关,故可以用滚动数组优化掉dp数组的第一维。
int solution(int n, std::vector<int> a, std::vector<int> b) {
using ll = long long;
vector<ll>dp(3,0);
dp[0]=1;
for(int i=0;i<n;i++)
{
vector<ll>ndp(3,0);
for(int j=0;j<3;j++)
{
int v=(j+a[i])%3;
ndp[v]=(ndp[v]+dp[j])%mod;
v=(j+b[i])%3;
ndp[v]=(ndp[v]+dp[j])%mod;
}
dp=ndp;
}
return dp[0];
}
时间复杂度O(3*n)=O(n)
390.小E的区间异或和
题解:
对每个二进制位单独考虑,则数组在当前二进制位下的数值为一个0,1数组 假设当前时第k位二进制 当第个位置为0,第个位置为1时,这一对0,1一共在个子串中出现,故这一对0,1的贡献为 。 1,0对的统计答案方式类似,故可以对每一位二进制位,设num0为当前位置之前的0的位置下标和,num1位当前位置之前1的位置下标和,若当前位置为1,则给答案加上,否则给答案加上。 最后返回答案即可。
const int mod=1e9+7;
int solution(int n, const std::vector<int>& a) {
using ll = long long;
ll ans = 0;
for (int j = 31; j >= 0; j--)
{
ll num1 = 0;
ll num0 = 0;
for (int i = 0; i < n; i++)
{
if (a[i] >> j & 1)
{
ans += 1LL * (n - i) * num0 % mod * (1LL << j) % mod;
ans %= mod;
num1 += i + 1;
num1 %= mod;
}
else
{
ans += 1LL * (n - i) * num1 % mod * (1LL << j) % mod;
ans %= mod;
num0 += i + 1;
num0 %= mod;
}
}
}
return ans;
}
时间复杂度O(n*logA)
小K的区间与值和
题解:
本题与上一题的区别在于这次的运算变成了与运算,那么有贡献的对只能是1和1,故设num表示前缀1的下标和,那么对每个1,答案加上最后输出答案即可。
using ll = long long;
using ull = unsigned long long;
const int mod = 1e9 + 7;
int solution(int n, vector<int> a) {
// write code here
ll ans = 0;
for (int j = 31; j >= 0; j--)
{
ll num = 0;
for (int i = 0; i < n; i++)
{
int v = a[i] >> j & 1;
if (v == 1)
{
ans += num * (1LL << j)%mod * (n - i)%mod;
ans%=mod;
num += i + 1;
num%=mod;
}
}
}
return ans;
}
248.小X的区间或值和
题解:
这道题变成了或运算,那么贡献计算变成了,如果当前位置为1,则答案加上,如果当前位置为0,则答案加上,每个位置统计完答案后,更新对应的位置下标和即可。
using ll = long long;
using ull = unsigned long long;
const int mod = 1e9 + 7;
int solution(int n, vector<int> a) {
// write code here
ll ans = 0;
for (int j = 31; j >= 0; j--)
{
ll sum = 0;
ll sum1 = 0;
for (int i = 0; i < n; i++)
{
if (a[i] >> j & 1)
{
sum1 += i + 1;
ans += sum * (n - i) * (1LL << j);
}
else
{
ans += sum1 * (n - i) * (1LL << j);
}
sum += i + 1;
}
}
return ans;
}
时间复杂度O(n*logA)