57分钟只做完了前三道
3216. 交换后字典序最小的字符串 - 力扣(LeetCode)
从左向右贪心即可
class Solution {
public:
string getSmallestString(string s) {
for(int i=1;i<s.size();i++){
if(s[i-1]>s[i] && (s[i-1]&1) == (s[i]&1) ){
swap(s[i-1],s[i]);
break;
}
}
return s;
}
};
去除掉只能交换相邻两个数和奇偶性的限制就是670. 最大交换 - 力扣(LeetCode)
为了让交换后的数最大要尽可能交换位置靠左的数,设这个数为下标为 i ,与它交换的数 j 应该是 i 右面最大的数,并且这个数要比 i 大。如果右面最大的数不只一个应该要让 j 尽可能靠右,例如19999,1应该和最后一个9交换。
综上 i 向右枚举,j 向左枚举即可得到下面的代码,时间复杂度为O(log^2 n),log n为这个数的长度
class Solution {
public:
int maximumSwap(int num) {
string s = to_string(num);
for(int i=0;i<s.size();i++){
int maxindex=i;
for(int j=s.size()-1;j>i;j--){
if(s[j]>s[maxindex])
maxindex=j;
}
if(maxindex!=i){
swap(s[i],s[maxindex]);
break;
}
}
return stoi(s);
}
};
上面的代码内层循环要多次寻找 i 之后的最大值,我们可以提前算出每个区间的最大值来省掉内层循环,代码如下,时间复杂度O( 2 * log n)
class Solution {
public:
int maximumSwap(int num) {
string s = to_string(num);
vector<int> max(s.size());//max[i]表示从max.size()~i中最大数的下标
max[s.size()-1]=s.size()-1;
for(int i=s.size()-2;i>=0;i--){
max[i]= s[i] > s[max[i+1]] ? i : max[i+1];//只有>更新,==不更新,保证了j尽可能靠右
}
for(int i=0;i<s.size()-1;i++){
if(s[i]<s[max[i+1]]){
swap(s[i],s[max[i]]);
break;
}
}
return stoi(s);
}
};
上面的代码节省了时间却消耗了额外的空间,其实可以边枚举 i 边更新 maxindex 来省去记录最大值的数组
class Solution {
public:
int maximumSwap(int num) {
string s=to_string(num);
int maxindex=s.size()-1,p=-1,q;
for(int i=s.size()-1;i>=0;i--){
if(s[i]>s[maxindex]){//更新maxindex,特别注意没有==
maxindex=i;
}else if(s[i]<s[maxindex]){//更新交换的下标对p,q
p=i;
q=maxindex;
}
}
if(p==-1) return num;//如果没有可以交换的下标对返回原数即可
swap(s[p],s[q]);
return stoi(s);
}
};
3217. 从链表中移除在数组中存在的节点 - 力扣(LeetCode)
经典链表删除节点
class Solution {
public:
ListNode* modifiedList(vector<int>& nums, ListNode* head) {
unordered_map<int,int> hash;
for(int val:nums){
hash[val]++;
}
ListNode dummy(0,head);
ListNode* cur=&dummy;
while(cur->next){
if( hash.count(cur->next->val) ){
cur->next=cur->next->next;
}else{
cur=cur->next;
}
}
return dummy.next;
}
};
3218. 切蛋糕的最小总开销 I - 力扣(LeetCode) 3219. 切蛋糕的最小总开销 II - 力扣(LeetCode)
每切一刀蛋糕就会多一块,要切成9块就需要8刀,所以不管怎么切总刀数是不变的,那我们就要让开销大的少切几次,开销小的多切几次。
查看样例可以发现竖着切了一刀以后横着的每个位置都需要多切一刀所以我们要先切花销大的 ,可以得到如下代码
class Solution {
public:
int cost;
void fun(int startm,int m,int startn,int n,vector<int>& horizontalCut, vector<int>& verticalCut){
if(startm+1==m && startn+1==n) return;
int Max=-1,flag=0,index=0;
for(int i=startm;i<m-1;i++){
if(Max<horizontalCut[i]){
Max=horizontalCut[i];
index=i;
}
}
for(int i=startn;i<n-1;i++){
if(Max<verticalCut[i]){
Max=verticalCut[i];
flag=1;
index=i;
}
}
cost+=Max;
if(flag==0){
fun(startm,index+1,startn,n,horizontalCut,verticalCut);
fun(index+1,m,startn,n,horizontalCut,verticalCut);
}else{
fun(startm,m,startn,index+1,horizontalCut,verticalCut);
fun(startm,m,index+1,n,horizontalCut,verticalCut);
}
}
int minimumCost(int m, int n, vector<int>& horizontalCut, vector<int>& verticalCut) {
fun(0,m,0,n,horizontalCut,verticalCut);
return cost;
}
};
上述代码递归处理了此问题,只能通过数据小的t3,再继续分析可以得出相同位置横着切的刀数其实就是竖着切的总刀数+1,可以得到如下代码
class Solution {
public:
long long minimumCost(int m, int n, vector<int>& horizontalCut, vector<int>& verticalCut) {
sort(horizontalCut.begin(),horizontalCut.end());
sort(verticalCut.begin(),verticalCut.end());
int i=horizontalCut.size()-1,j=verticalCut.size()-1,cnth=1,cntv=1;
long long ans=0;
while( i>=0 || j>=0 ){
if( i<0 || ( j>=0 && horizontalCut[i] < verticalCut[j]) ){
ans += cntv * verticalCut[j];
cnth++;
j--;
}else{
ans += cnth * horizontalCut[i];
cntv++;
i--;
}
}
return ans;
}
};