Description
问选择一个序列上的所有数的最少操作次数,跟电脑上选择文件一样,输出操作方案.
Sol
贪心.
用Shift一段文件只能使用一次,之后必须一直按Ctrl了.
然后就是看用Shift选哪段区间了.
如果选 \((l,r)\) 那么答案就是 \((C_{l-1})+(C_{n}-C_{r})+(r-l+1-C_{r}+C_{l-1})+2\) .
化简就是 \(C_{n}+r-2C_{r}-(l-1)+2C_{l-1}+2\)
这个决策点是唯一的,只需要计算答案的时候顺便统计一下 \((l-1)+2C_{l-1}\) 就可以了.
否则就是用Ctrl全选上.
复杂度 \(O(n)\)
Code
#include<cstdio>
#include<iostream>
using namespace std;
const int N = 100005;
int n;
char c[N];
int s[N];
int ans[3];
int main(){
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d",&n);scanf("%s",c+1);
for(int i=1;i<=n;i++) s[i]=s[i-1]+(c[i] == '*');
// for(int i=1;i<=n;i++) cout<<s[i]<<" ";cout<<endl;
int tmp=0,u;ans[0]=n+2;
for(int i=1;i<=n;i++){
u=s[n]-2*s[i]+i+2;
if(2*s[tmp]-tmp+u < ans[0]){
ans[1]=tmp+1,ans[2]=i;
ans[0]=2*s[tmp]-tmp+u;
}
if(2*s[tmp]-tmp > 2*s[i]-i) tmp=i;
}
if(ans[0]<s[n]){
printf("%d\n",ans[0]);
printf("%d\n",ans[1]);
printf("Shift+%d\n",ans[2]);
for(int i=1;i<ans[1];i++) if(s[i]-s[i-1]) printf("Ctrl+%d\n",i);
for(int i=ans[1];i<=ans[2];i++) if(s[i]-s[i-1]==0) printf("Ctrl+%d\n",i);
for(int i=ans[2]+1;i<=n;i++) if(s[i]-s[i-1]) printf("Ctrl+%d\n",i);
}else{
printf("%d\n",s[n]);
int f=0;
for(int i=1;i<=n;i++){
if(s[i]-s[i-1]){
if(!f) printf("%d\n",i),f=1;
else printf("Ctrl+%d\n",i);
}
}
}
return 0;
}