E. Binary Deque
题目大意:
01数组,用类似双向队列的出队方式,即从begin、end开始渐渐出队。问最少多少次操作,可以让剩下数组和为s。
解题思路: 因为是双向队列,所以只删除left、right一个方向的数组是不对的。但是每次删除元素要考虑左右等。可以考虑用双指针算法。先求前缀和,快慢指针遍历,当快指针的前缀和元素减去慢指针前缀和元素大于s的时候停止快指针的迭代,之后判断用的操作次数,用总长度减去快慢指针区间就是等于s的操作次数。就这样一直遍历、迭代,找到次数最小的即为答案。
int a[N];
void solve() {
memset(a, 0, sizeof(a)); // 每次清零
int n,s; cin>>n>>s;
rep(i,1,n) {scanf("%d", &a[i]); a[i] += a[i-1]; } // 读入后进行前缀和
int ma(0x3f3f3f3f); // 操作次数
for(int i = 1, j = 1; i <= n; ++i) { // i -- 慢指针; j -- 快指针;
while(j < n && a[j + 1] - a[i - 1] <= s) j++; // 快指针迭代,找到大于s的区间;
if(a[j] - a[i - 1] == s) { // 如果区间和等于s,找最小操作次数
ma = min(ma, n - (j - i + 1));
}
}
// 不等于 0x3f3f3f3f 表示有答案,否则,无解
cout<<(ma < INF ? ma : -1)<<"\n";
}
C. Sum of Substrings
题目大意:
一个01字符串,将字符串sisi+1用十进制表示并累加求和。
可进行操作:
将相邻两个字符进行交换。
现,给出最多操作次数,问最小值多少。
解题思路: 观察可以发现,如果字符串第一个元素是1,那么对求和的贡献是10,如果字符串最后一个元素是1,对求和的贡献值是1,其他位置的字符串为1时,贡献是11。(x1, 1x – 1 ,10)。在进行操作的时候,只用考虑begin和end的位置就可以,因为中间怎么交换都是11。可以考虑对字符串第一个位置进行++判断,找到第一个为1的字符位置,对end用–,找到第一个为1的字符位置。
void solve() {
int n, k; cin>>n>>k;
string str; cin>>str;
int len1 = count(str.begin(), str.end(), '1'); // 字符串中有多少个1
LL sum = 0;
int le = 0, ri = str.size()-1;
while(str[le] == '0') le++; // 从前往后找第一个为1的字符位置
while(str[ri] == '0') ri--; // 从后往前找第一个为1的字符位置
sum = le + n - ri - 1; // begin和end查找到需要的操作次数之和
int ans = len1*11; // 按1都是在中中间
if(len1 == 0) { // 没有一个‘1’,就是0;return
cout<<0<<endl; return ;
}
// 四种情况:
// begin end 可以完全进行操作
// end 可以 -- 先end --- 10
// begin 可以 - 1--
// 都不可以
// begin和end的操作次数和小于等于最大操作次数
if(k >= sum && le != ri) ans -= 11;
// end的操作次数小于k
else if(k >= n - ri - 1) ans -= 10;
// begin的操作次数小于k
else if(k >= le) ans -= 1;
cout<<ans<<endl;
}
C. Elemental Decompress
题目大意:
两个序列的每个位置的最大元素组成的数组已给定。求两个序列。
解题思路: 序列内元素不重复。对于给定的数组,序列元素出现的次数是 0, 1, 2。并且次数为0的元素个数一定跟次数为2的元素个数相同,否则两个序列构不成给定数组。同时,0出现元素和2出现元素一定全部满足出现次数2的元素大于出现次数0的元素。 可以考虑将出现次数为1的元素他们两个序列的位置上的元素相同。0和2的进行互补。
void solve() {
int n; cin>>n;
rep(i,1,n) { // 初始化
a[i] = p1[i] = p2[i] = ha[i] = hs[i] = _cnt[i] = 0;
}
rep(i,1,n) {
cin>>a[i]; // 读入给定数组;
ha[a[i]] ++; // 每一个元素出现次数 0 1 2;
}
rep(i,1,n) {
if(ha[i] > 2) { // 有大于2的元素次数存在表示不存在两个序列
puts("NO");
return ;
}
}
vector<int> zero,two; // 0 2 出现元素读入
rep(i,1,n) {
if(ha[i] == 2) two.push_back(i);
if(ha[i] == 0) zero.push_back(i);
}
if(zero.size() != two.size()) { // 0 2 个数不同,“No”
puts("NO");
return ;
}
// sort 为2 0 分配两个序列
sort(zero.begin(), zero.end());
sort(two.begin(), two.end());
int len = zero.size() -1;
rep(i,0,len) { // 出现次数2的元素大于出现次数1的元素
// 元素为2的元素映射的元素为1的元素、
if(two.at(i) > zero.at(i)) hs[two[i]] = zero[i];
else {
puts("NO");
return ;
}
}
rep(i,1,n) { // 出现次数为1的元素,两个序列的位置元素为该元素
if(ha[a[i]] == 1) p1[i] = p2[i] = a[i];
}
rep(i,1,n) {
_cnt[a[i]] ++; // 已出现次数
// 出现一次, 但是数组中有两个该元素,
if(_cnt[a[i]] == 1 && ha[a[i]] == 2) p1[i] = a[i], p2[i] = hs[a[i]];
// 出现两次,数组有2个该元素
// 赋值 互换赋值第一次出现时映射
if(_cnt[a[i]] == 2 && ha[a[i]] == 2) p2[i] = a[i], p1[i] = hs[a[i]];
}
puts("YES");
rep(i,1,n) cout<<p1[i]<<" "; puts("");
rep(i,1,n) cout<<p2[i] <<" "; puts("");
}
总结
双指针板子题会,要构造双指针就不会喽,没有找到题思路与双指针算法之间的联系。
做题思维太固定,死板,一个思路想半天不对,不去换思路。以后做题应该多换思路,碰idea。
做题太少,导致做题没有思路,会再多数据结构、算法,没有做大量题刷思路简单算法都很难想到。
寒假每天至少做两道cf题,并写题解和反思。