1.PREV-6 翻硬币(贪心)
问题描述
小明正在玩一个“翻硬币”的游戏。
桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。
比如,可能情形是:**oo***oooo
如果同时翻转左边的两个硬币,则变为:oooo***oooo
现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?
我们约定:把翻动相邻的两个硬币叫做一步操作,那么要求:
输入格式
两行等长的字符串,分别表示初始状态和要达到的目标状态。每行的长度<1000
输出格式
一个整数,表示最小操作步数。
样例输入1
**********
o****o****
样例输出1
5
样例输入2
*o**o***o***
*o***o**o***
样例输出2
1
分析:从第一个开始看,需要翻的就把它和它后面那个一起翻过来,继续向后看即可。因为最左边那个必须要和后面一起翻,并且对于中间的一个,可以证明必须和后面那个一起翻,假设和前面的一个一起翻过来的话,那么因为前面都是翻好的,必定不能翻到目标结果。因此就是贪心,代码很简单:
#include<bits/stdc++.h>
using namespace std;
void res(char &c){
if(c == 'o') c = '*';
else c = 'o';
}
int main(){
string str1,str2;
while(cin >> str1 >> str2){
int cnt = 0;
int len = str1.length();
for(int i = 0;i < len;i++){
if(str1[i] != str2[i]){
cnt++;
res(str1[i + 1]);
}
}
cout << cnt << endl;
}
return 0;
}
2.PREV-7连号区间数(思维)
问题描述
小明这些天一直在思考这样一个奇怪而有趣的问题:
在1~N的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是:
如果区间[L, R] 里的所有元素(即此排列的第L个到第R个元素)递增排序后能得到一个长度为R-L+1的“连续”数列,则称这个区间连号区间。
当N很小的时候,小明可以很快地算出答案,但是当N变大的时候,问题就不是那么简单了,现在小明需要你的帮助。
输入格式
第一行是一个正整数N (1 <= N <= 50000), 表示全排列的规模。
第二行是N个不同的数字Pi(1 <= Pi <= N), 表示这N个数字的某一全排列。
输出格式
输出一个整数,表示不同连号区间的数目。
样例输入1
4
3 2 4 1
样例输出1
7
样例输入2
5
3 4 2 5 1
样例输出2
9
分析:能构成连号区间的条件是区间中最大减最小的是区间的长度 - 1(因为连号区间排序后是等差数列d = 1),然后分析要两个for循环,5e4/1s感觉会超时qwq,但是它确实过了,不知道为啥qwq
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 6;
int arr[maxn];
int main(){
int n;
while(cin>>n){
int cnt = n;
for(int i = 0;i < n;i++)
cin>>arr[i];
for(int i = 0;i < n - 1;i++){
int maxx = arr[i];
int minn = arr[i];
for(int j = i + 1;j < n;j++){
maxx = max(maxx,arr[j]);
minn = min(minn,arr[j]);
if(maxx - minn == j - i) cnt++;
}
}
cout << cnt << endl;
}
return 0;
}
3.PREV-8买不到的数目(数论)
问题描述
小明开了一家糖果店。他别出心裁:把水果糖包成4颗一包和7颗一包的两种。糖果不能拆包卖。
小朋友来买糖的时候,他就用这两种包装来组合。当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。
你可以用计算机测试一下,在这种包装情况下,最大不能买到的数量是17。大于17的任何数字都可以用4和7组合出来。
本题的要求就是在已知两个包装的数量时,求最大不能组合出的数字。
输入格式
两个正整数,表示每种包装中糖的颗数(都不多于1000)
输出格式
一个正整数,表示最大不能买到的糖数
样例输入1
4 7
样例输出1
17
样例输入2
3 5
样例输出2
7
这个题没有说gcd(a,b) == 1,qwq
不能表示成ax + by形式(x >= 0,y >= 0,x,y为整数)的最大整数为a*b - a - b;
证明:
1.ax + by = ab - a - b无解:
假设此方程有解,则有ax + b(y + 1) = a(b + 1),即a(x - b + 1) = -b(y + 1),则可知a能够整除b(y + 1),进而由gcd(a,b) = 1可知a能够整除(y + 1),此时因为y + 1 > 0,a > 0 所以 y + 1 = aq(q > 0) >= a,这时再带回原方程中有:
则与原假设矛盾,所以无解,得证
2.ax + by = ab - a - b + d(d >= 1)必定有解:
首先,因为gcd(a,b) = 1,以及裴蜀定理,可知方程ax + by = 1*d (d >= 1)必然有无穷个整数解,取其一个假设为:
此时再看原方程,将d的解带入原方程有:
找到满足条件的原方程的解,得证
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b;
while(cin >> a >> b){
cout << a*b - a - b << endl;
}
return 0;
}
4.裴蜀定理
裴蜀定理说明了对任何整数a、b和它们的最大公约数d,关于未知数x和y的线性不定方程(称为裴蜀等式):若a,b是整数,且(a,b)=d,那么对于任意的整数x,y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立。
它的一个重要推论是:a,b互质的充要条件是存在整数x,y使ax+by=1.
在数论中,裴蜀定理是一个关于最大公约数(或最大公约式)的定理。裴蜀定理说明了对任何整数a、b和它们的最大公约数d,关于未知数x和y的线性丢番图方程(称为裴蜀等式):
ax + by = m
有解当且仅当m是d的倍数。裴蜀等式有解时必然有无穷多个整数解,每组解x、y都称为裴蜀数
5.扩展欧几里得定理
对于不完全为 0 的整数 a,b,gcd(a,b)表示 a,b 的最大公约数。那么一定存在整数 x,y 使得 gcd(a,b)=ax + by。
#include<bits/stdc++.h>
using namespace std;
int ex_gcd(int a,int b,int &x,int &y){
if (b == 0){
x = 1,y = 0;
return a;
}
int q = ex_gcd(b,a % b,y,x);
y -= a/b*x;
return q;//q为gcd(a,b)
}
int main(){
int a,b,x,y;
while(cin>>a>>b){
ex_gcd(a,b,x,y);
cout << x << " " << y << endl;
}
return 0;
}
计算方法:
首先有这个性质(证明下次补上去qwq,吃饭~):gcd(a, b) = gcd(b, a % b)
然后就有这样的方程组:
gcd(a, b) = gcd(b, a % b) ax + by = gcd(a,b) //为啥latex公式编辑不能用了qwq
则有下面的:注意:a % b = a - (a / b)*b
则可以采用递归来求解方程,基线条件为:b == 0,即ax = a即x = 1,y = 0
扩展欧几里得求得逆元:
ax≡1 (mod p)即ax-yp=1.把y写成+的形式就是ax+py=1,就表示x是a的模p乘法逆元
#include<bits/stdc++.h>
using namespace std;
int ex_gcd(int a,int b,int &x,int &y){
if (b == 0){
x = 1,y = 0;
return a;
}
int q = ex_gcd(b,a % b,y,x);
y -= a/b*x;
return q;//q为gcd(a,b)
}
int inv(int a, int p){
int d, x, y;
d = ex_gcd(a, p, x, y);
return d == 1 ? (x + p) % p : -1;
}
int main() {
int a,p;
while(cin>>a>>p){
cout << inv(a,p) << endl;
}
return 0;
}