寒假力扣周赛
第300场周赛
3-通过最少操作次数使数组的和相等:双指针+贪心
大意:给定两个数组a,b,里面的元素都是1-6之间的数,每次操作可以对任意数组中的任意元素修改为1-6之间的数,问最少操作数使得两数组的元素和相等。
思路:假设a的和比b的和大,那先对a从大到小排序,对b从小到大排序,求出差值ca,l指向a中未修改且最靠前的元素,r指向b中未修改且最靠前的元素,贪心策略为把a中的元素尽量修改为1,b中的元素尽量修改为6,前提条件是先比较a[l]-1,6-a[r]的大小。
class Solution {
public:
bool cmp(int a,int b)
{
return a > b;
}
vector<int>a,b;
int minOperations(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
int m = nums2.size();
int tmp1 = 0,tmp2 = 0;
if(n > m){
if(n > m*6) return -1;
}
if(n < m){
if(m > 6*n) return -1;
}
for(int i = 0; i < n; i++){
tmp1 += nums1[i];
}
for(int i = 0; i < m; i++){
tmp2 += nums2[i];
}
int ans = 0;
int ca;
if(tmp1 > tmp2){
a = nums1;
b = nums2;
}
else{
a = nums2;
b = nums1;
}
sort(a.begin(),a.end());
sort(b.begin(),b.end());
reverse(a.begin(),a.end());
ca = abs(tmp1 - tmp2);
int l = 0,r = 0,x,y;
n = a.size();
m = b.size();
while(l < n && r < m){
x = a[l]-1;
y = 6-b[r];
if(x < y){
if(ca <= 0) return ans;
ca -= y;
r++;
ans++;
}
else if(x > y){
if(ca <= 0) return ans;
ca -= x;
l++;
ans++;
}
else {
if(ca <= 0) return ans;
ca -= x;
l++;
ans++;
}
if(ca <= 0) return ans;
}
while(l < n){
x = a[l]-1;
if(ca <= 0) return ans;
ca -= x;
l++;
ans++;
}
while(r < m){
y = 6-b[r];
if(ca <= 0) return ans;
ca -= y;
r++;
ans++;
}
return ans;
}
}; 第229场周赛
3-执行乘法运算的最大分数:动态规划
大意:给定一个数组nums和另一个数组multipliers,第i次可以从nums中的左端或右端取一个数和multipliers中的第i个数相乘,问最后所有结果相加和最大是多少。
思路:要么从左取要么从右取,两种选择,数据范围不大,考虑动态规划做。
找状态:dp[i][j] 表示选第k个数是有i个左端点,j个右端点。假设i+j=k
状态转移:如果i=0,也就是全取的是右端点,当前只能取右端点,那么dp[i][k-j] = dp[i][k-j-1]+a[m-k+i]*b[k-1];
如果i=k,也就是全是做端点,当前只能取左端点,那么dp[i][k-i] = dp[i-1][k-i]+a[i-1]*b[k-1];
其它情况dp[i][k-i] = max(dp[i][k-i-1]+a[m-k+i]*b[k-1], dp[i-1][k-i]+a[i-1]*b[k-1]);两者取最大中的一个。
class Solution {
public:
int a[100005],b[100005];
int dp[2005][2005];
int maximumScore(vector<int>& nums, vector<int>& multipliers) {
int n = multipliers.size();
int m = nums.size();
for(int i = 0; i < nums.size(); i++) a[i] = nums[i];
for(int i = 0; i < multipliers.size(); i++) b[i] = multipliers[i];
int ans = -1e9;
memset(dp,0,sizeof(dp));
for(int k = 1; k <= n; k++){
for(int i = 0; i <= k; i++){
if(i == 0) dp[i][k-i] = dp[i][k-i-1]+a[m-k+i]*b[k-1];
else if(i == k) dp[i][k-i] = dp[i-1][k-i]+a[i-1]*b[k-1];
else{
dp[i][k-i] = max(dp[i][k-i-1]+a[m-k+i]*b[k-1], dp[i-1][k-i]+a[i-1]*b[k-1]);
}
if(k == n) ans = max(dp[i][k-i],ans);
}
}
return ans;
}
}; 4-由子序列构造的最长回文串的长度:动态规划
大意:给定两个字符串s1,s2,从s1中取出一个非空子序列,再从s2中取出一个非空子序列,问连接后的字符串是回文串吗?最长是多少。
思路:直接将s1和s2先连起来,再求回文子序列的最长长度,注意一端要在s1内,另一端要在s2内。
class Solution {
public:
int dp[2005][2005];
int longestPalindrome(string word1, string word2) {
string s = word1+word2;
int n = s.size();
int n1 = word1.size()-1;
int n2 = word2.size()-1;
int ans = 0;
for(int i = n-1; i >= 0; i--){//由小区间扩展到大区间,所以倒过来。
dp[i][i] = 1;
for(int j = i+1; j < n; j++){
if(s[i] == s[j]){
dp[i][j] = dp[i+1][j-1]+2;
if(i <= n1 && j > n1) ans = max(dp[i][j],ans);
}
else dp[i][j] = max(dp[i+1][j],dp[i][j-1]);
}
}
return ans;
}
}; 第227场周赛
1-检查数组是否经排序和轮转得到
大意:问是否存在k使得所有数循环移动(i+k)%nums.size()后,使得数组有序。
思路:暴力枚举原数组中的每个数,看看是否存在以某个数为起点使得数组有序。
class Solution {
public:
bool check(vector<int>& nums) {
int a[105];
vector<int>num = nums;
int t = nums[0],xb = 0;
for(int i = 1; i < nums.size(); i++){
if(nums[i] < t){
xb = i;
t = nums[i];
}
}
// cout << xb;
for(int k = 0; k < num.size(); k++){
int q = 0;
for(int i = k; i < num.size(); i++){
a[q++] = num[i];
}
for(int i = 0; i < k; i++){
a[q++] = num[i];
}
int flag = 0;
sort(nums.begin(),nums.end());
for(int i = 0; i < nums.size(); i++){
// cout << a[i] << " ";
if(a[i] != nums[i]) {
flag = 1;
break;
}
}
if(!flag) return true;
}
return false;
}
}; 2-移除石子的最大得分
大意:给出三堆石子,每次从不同的两堆中个取走一个石子,并加一分,问最大得分是多少。
class Solution {
public:
int maximumScore(int a, int b, int c) {
int num[10];
num[0] = a;
num[1] = b;
num[2] = c;
sort(num,num+3);
if(num[0]+num[1] < num[2]) return num[0]+num[1];
if(num[0]+num[1] == num[2]) return num[2];
return (num[0]+num[1]-num[2])/2+num[2];
}
}; 3-构造字典序最大的合并字符串
大意:给定两个字符串s1,s2,有两种操作,第一种是取s1的第一个字符加入merge尾部并从s1中删去,第二种是取s2的第一个字符加入merge尾部并从s2中删去,求merge最大字典序的字符串是多少。
思路:弄两个指针l1,l2分别指向s1,s2的第一个字符,判断s1[l1],s2[l2]大小,取最大的那个,如果相同,那么继续判断后面的字符,谁大就取谁。(注意边界如s1="ababa",s2="ababa",答案merge="ababababa")
class Solution {
public:
string largestMerge(string word1, string word2) {
int l1,l2,r1,r2;
string ans;
l1 = l2 = 0;
int n1,n2;
n1 = word1.size();
n2 = word2.size();
ans = "";
while(l1 < n1 && l2 < n2){
if(word1[l1] < word2[l2]){
ans += word2[l2];
l2++;
continue;
}
if(word1[l1] > word2[l2]){
ans += word1[l1];
l1++;
continue;
}
int t1,t2;
// cout << l1 << " " << l2 << endl;
t1 = l1;
t2 = l2;
while(word1[t1] == word2[t2] && t1 < n1 && t2 < n2){
// cout << word1[t1] << " " << word2[t2] << "\n";
t1++;
t2++;
}
if(t1 == n1 && t2 == n2){
ans += word1[l1];
l1++;
}
if(t1 == n1) word1 += "A";
if(t2 == n2) word2 += "A";
if(word1[t1] < word2[t2]){
ans += word2[l2];
l2++;
continue;
}
if(word1[t1] > word2[t2]){
ans += word1[l1];
l1++;
}
}
if(l1 < n1){
for(int i = l1; i < n1; i++) ans += word1[i];
}
if(l2 < n2){
for(int i = l2; i < n2; i++) ans += word2[i];
}
return ans;
}
}; 4-最接近目标值的子序列和:dfs+二分
大意:给定一组数,从中取若干个数,使得这些数的和和goal最接近。
思路:看到数据范围是40,又是取若干个数,首先想到的是dfs+剪枝,可是还是TLE,那就试着把范围缩小,将数组对半分成两个数组,对这两个数组dfs暴搜出所有和的情况,然后对其中一个数组排序,枚举另一个数组中的每一个数,因为要尽可能让lsum+rsum>=goal,所以在排好序的数组中查找第一个大于等于goal-rsum,还有可能在这个位置的前一个。
class Solution {
public:
int n,g,xb;
long long ans = 1e15;
vector<int>num;
vector<int>num1[5];
void dfs(int cur,int en,int res,int bh)
{
if(cur > en) return ;
num1[bh].push_back(res);
for(int i = cur+1; i <= en; i++){
dfs(i,en,res+num[i],bh);
}
}
int minAbsDifference(vector<int>& nums, int goal) {
n = nums.size();
num = nums;
for(int i = 0; i <= n/2-1; i++){
dfs(i,n/2-1,nums[i],1);
}
for(int i = n/2; i <= n-1; i++){
dfs(i,n-1,nums[i],2);
}
sort(num1[2].begin(),num1[2].end());
int ans = 1e9+7;
for(int i = 0; i < num1[1].size(); i++){
// cout << num1[1][i] << "\n";
int temp = goal-num1[1][i];
int xb = lower_bound(num1[2].begin(),num1[2].end(),temp)-num1[2].begin();
// cout << xb << endl;
ans = min(ans,abs(num1[1][i]-goal));
ans = min(ans,abs(num1[2][xb]+num1[1][i]-goal));
if(xb >= 1){
ans = min(ans,abs(num1[2][xb-1]+num1[1][i]-goal));
}
}
for(int i = 0; i < num1[2].size(); i++){
ans = min(ans,abs(num1[2][i]-goal));
}
return min(ans,abs(goal));
}
}; 第45场双周赛
https://leetcode-cn.com/contest/biweekly-contest-45/2-任意子数组和的绝对值的最大值
大意:给定一组数,问子数组中和的绝对值的最大值是多少。
思路:先求出nums的前缀和pre,然后对pre排序,如果pre中全是负数,那么取pre中最小数的绝对值,如果全是正数,那么取pre中最大的数,如果即有正又有负,取pre中最大值减去最小值作为答案。
class Solution {
public:
int pre[100005];
int maxAbsoluteSum(vector<int>& nums) {
pre[0] = nums[0];
for(int i = 1; i < nums.size(); i++){
pre[i] = nums[i]+pre[i-1];
}
sort(pre,pre+nums.size());
if(nums.size() == 1) return abs(nums[0]);
if(pre[0] >= 0) return pre[nums.size()-1];
if(pre[nums.size()-1] < 0) return abs(pre[0]);
return max(abs(pre[nums.size()-1]-pre[0]),abs(pre[nums.size()-1]));
}
}; 3-删除字符串两端相同字符后的最短长度
class Solution {
public:
int minimumLength(string s) {
int l = 0,r = s.length()-1;
// if(r == 0) return 1;
while(l <= r){
if(l == r){
if(s[r] == s[r+1]) return 0;
return 1;
}
if(s[l] == s[r]){
l++;
r--;
}
else{
if(l == 0 || r == s.length()-1) return s.length();
if(s[l-1] == s[l]){
l++;
}
else if(s[r+1] == s[r]){
r--;
}
else{
break;
}
}
}
return r-l+1;
}
}; 4:最多可以参加的会议数目 II:动态规划+二分
思路:一看这种考虑选或不选,数据范围也不小的题,可以考虑用dp来解决。
先将会议按结束时间从小到大排序。
状态:dp[i][j]表示前i个会议中参加了j个的最大价值和。
状态转移:
1)第i个会议不参加,那么dp[i][j] = dp[i-1][j]。
3)参加第i个会议,从前i个会议中二分找到会议结束时间首先小于第i个会议开始时间的会议l,dp[i][j] = dp[l][j-1],为什么不是dp[i][j] = dp[i-1][j-1]呢,因为如果第i-1个会议和第i个会议冲突的话,答案会错误,也就是前面的状态对后面的状态有影响。
class Solution {
public:
struct Node{
int s,e,v;
bool operator<(const Node &x)const{
return x.e > e;
}
}node[1000005];
int s[1000005],e[1000005],v[1000005];
int maxValue(vector<vector<int>>& events, int k) {
vector<int>dp[1000005];
for(int i = 0; i < events.size(); i++)
for(int j = 0; j <= k; j++) dp[i].push_back(0);
for(int i = 0; i < events.size(); i++){
node[i].s = events[i][0];
node[i].e = events[i][1];
node[i].v = events[i][2];
}
sort(node,node+events.size());
dp[0][0] = 0;
dp[0][1] = node[0].v;
//cout <<node[2].e << endl;
for(int i = 1; i < events.size(); i++){
for(int j = 1; j <= k; j++){//不选
dp[i][j] = max(dp[i][j],dp[i-1][j]);
}
int l = 0,r = i,ans = -1;
while(l <= r){
int mid = (l+r)>>1;
if(node[mid].e < node[i].s){
ans = mid;
l = mid+1;
}
else r = mid-1;
}
if(ans == -1){
dp[i][1] = max(dp[i][1],node[i].v);
continue;
}
for(int j = 1; j <= k; j++){
dp[i][j] = max(dp[i][j],dp[ans][j-1]+node[i].v);
}
}
int ans = 0;
for(int j = 0; j <= k; j++){
ans = max(ans,dp[events.size()-1][j]);
}
return ans;
}
}; 
美团公司福利 3017人发布