文章目录
A - A Variety of Operations
题意
给定两个数a、b,初始都是0,能做以下操作:
- 同时 +k
- a+k, b-k
- a-k, b+k
问能经过几次操作能使ab变为c和d
题解
我们知道,因为两个数一开始都是0,如果同时增加k的话,那么肯定就不会使得两个数之间的差变小,所以只能靠后两个步骤来进行
那么一个数+k,另一个数-k,也就是说两个数的差变为了 2k。我们知道 2k 一定是偶数,所以,如果c d 两个数的差是奇数,那么一定变不出来
那么因为k是任意数,我们只需要把两个数进行一次第二或第三部操作使其差变为c d之间的差,然后再同时加上一个数,就能使其变为c d,一共2次
0和1次就很简单了
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#include <unordered_map>
#define ll long long
#define ull unsigned long long
#define re return
#define pb push_back
#define Endl "\n"
#define endl "\n"
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
const int M = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int T;
int a, b, c, d;
int main(){
cin >> T;
while(T--){
cin >> c >> d;
a = b = 0;
if(abs(c - d) % 2 == 1){
cout << -1 << endl;
continue;
}
if(c == 0 && d == 0)
cout << 0 << endl;
else {
if(c == d){
cout << 1 << endl;
}
else
cout << 2 << endl;
}
}
}
B - Take Your Places!
题意
给你一个数列,只能交换相邻两个位置的元素,问最少交换多少次使得这个数列能够满足奇偶相间
题解
首先,如果这个数列奇数和偶数的个数相差大于1,那么肯定最后一定会有相邻的
那么就可以根据奇数和偶数的个数来进行分类讨论
假设奇数比偶数个数多,lj为奇数,lo为偶数,lj > lo
假设长度为n = 5,那么答案肯定最后是10101
,1代表奇数
假如给你一个是11100
我们知道第一个1的位置是1,第二个1的位置是3,第三个1的位置是5,所以最后的答案就是3-2+5-3=3
所谓的交换其实就可以理解为一个数向前移动一个
转自上篇题解中的巨巨
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#include <unordered_map>
#define ll long long
#define ull unsigned long long
#define re return
#define pb push_back
#define Endl "\n"
#define endl "\n"
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
const int M = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int T;
int n;
int main(){
cin >> T;
while(T--){
vector<int> ou;
vector<int> ji;
cin >> n;
for (int i = 0; i < n; i++){
int x;
cin >> x;
if(x & 1)
ji.pb(i);
else
ou.pb(i);
}
if(abs((int)ji.size() - (int)ou.size()) >= 2){
cout << -1 << endl;
continue;
}
int ans = 0;
if(ji.size() > ou.size()){
int id = 0;
for (int i = 0; i < ji.size(); i++, id += 2){
ans += abs((int)ji[i] - id);
}
}
else if(ji.size() < ou.size()){
int id = 0;
for (int i = 0; i < ou.size(); i++, id += 2){
ans += abs(ou[i] - id);
}
}
else{
int id = 0;
for (int i = 0; i < ji.size(); i++, id += 2){
ans += abs(ji[i] - id);
}
id = 0;
int ans1 = 0;
for (int i = 0; i < ou.size(); i++, id += 2){
ans1 += abs(ou[i] - id);
}
ans = min(ans, ans1);
}
cout << ans << endl;
}
}
C - Compressed Bracket Sequence
参考:https://blog.csdn.net/weixin_45775438/article/details/119998607
题意
给定一个数组c,奇数位代表有多少个左括号,偶数位代表有多少个右括号。问能够匹配的区间个数是多少
题解
首先我们想答案的组成:
((())))
,左括号有3个,右括号有四个,那么答案就是min(3,4)
(((())(())
,这个与上面的区别在于出现了左右括号交替的现象,那么答案除了相邻直接去min
外,还要考虑与两个相邻区间内共同组成的答案,例如:()()
根据上面的思路和以前的做题经验,我觉得这种统计区间的题目类型,其中常用的做法就是固定一个端点来进行枚举答案,或者是累积答案
根据大佬的题解,我们可以先固定一个左端点 i ,表示在以这个为左端点的答案进行统计,设 a i = z u o a_i=zuo ai=zuo,枚举 j 从 i + 1 到 n
设此时影响 i 为左端点的多余的左括号为 duo,一开始 duo = 0
-
如果 j 是奇数,那么我们 d u o + = a [ j ] duo+=a[j] duo+=a[j],表示我们必须要把这些多的左括号匹配完成后才能得到我们上面设的以 i i i为左端点得到的答案
-
如果 j 是偶数,那么我们要优先把 duo 的左括号匹配完才能做在以 i i i为左端点的答案。
如果 duo 的左括号匹配完了,此时右括号还剩 t m p − m i n ( t m p , d u o ) tmp-min(tmp,duo) tmp−min(tmp,duo),那么,就可以累积答案了,即 a n s + = m i n ( z u o , t m p ) ans+=min(zuo, tmp) ans+=min(zuo,tmp)
如果此时右括号 t m p tmp tmp有剩余,那么就可以直接判断 i + 1 i+1 i+1了,因为右括号有多的左边无论如何也不能将其抵消
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#include <unordered_map>
#define ll long long
#define ull unsigned long long
#define re return
#define pb push_back
#define Endl "\n"
#define endl "\n"
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1e3 + 10;
const int M = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int n;
int a[N];
int main(){
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
ll ans = 0; // c_i 的取值范围为1e9,大概率会爆int
for (int i = 1; i <= n; i += 2){
ll zuo = a[i];
ll duo = 0;
for (int j = i + 1; j <= n; j++){
if(j & 1){ // 奇数位为左括号
duo += a[j];
}
else{
ll tmp = a[j];
ll x = min(tmp, duo);
duo -= x;
tmp -= x;
if(!duo){
x = min(zuo, tmp);
ans += x;
zuo -= x;
tmp -= x;
if(j != i + 1)
ans++;
if(tmp)
break;
}
}
}
}
cout << ans;
}