寒假happy4-扩展欧几里得定理,裴蜀定理,推论,PREV-6 翻硬币(贪心)PREV-7连号区间数(思维)PREV-8买不到的数目(数论)

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,这时再带回原方程中有:

ax + (y + 1)b \geq ax + ab \geq 0*x + ab \geq ab > ab - a

则与原假设矛盾,所以无解,得证

2.ax + by = ab - a - b + d(d >= 1)必定有解:

首先,因为gcd(a,b) = 1,以及裴蜀定理,可知方程ax + by = 1*d (d  >= 1)必然有无穷个整数解,取其一个假设为:

ax_{0} + by_{0} = d (x_{0} >= 1, y_{0} >= 1)

此时再看原方程,将d的解带入原方程有:

\small ab - a - b + d = ab - a - b + (ax_{0} + by_{0}) = a(x_{0} - 1) + b(a + y_{0} - 1) (x_{0} \geqslant 1, y_{0} \geqslant 1)

找到满足条件的原方程的解,得证

#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.

\small \left \{(\frac{m}{d}(x_{0}+\frac{kb}{d}),\frac{m}{d}(y_{0}-\frac{ka}{d})) | k\epsilon Z\right \}

在数论中,裴蜀定理是一个关于最大公约数(或最大公约式)的定理。裴蜀定理说明了对任何整数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

\small ax + by = gcd(b,a \% b) = bx^{'} + (a \% b)y^{'} = bx^{'}+(a-\frac{a}{b}*b)y^{'}

\small ax + by = ay^{'}+b(x^{'} - \frac{a}{b}*by^{'} )

\small x = x^{'} \\ y = x^{'} - \frac{a}{b}*y^{'}

则可以采用递归来求解方程,基线条件为: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;
}

猜你喜欢

转载自blog.csdn.net/guifei0/article/details/86654799