NOI2002荒岛野人 (savage)——拓展欧几里得

题目描述
这里写图片描述

输入

这里写图片描述

输出

这里写图片描述

样例输入
3
1 3 4
2 7 3
3 2 1
样例输出
6

题解

因为岛是环形的,自然要 mod m
考虑两个野人 i,j,如果他们同时出现在一个山洞,那么 Ci+x*Pi=Cj+x*Pj(mod m), 移项后可得(Pi-Pj)*x+m*y=Cj-Ci
把 Pi-Pj 看成 a,m 看成 b,Cj-Ci 看成 c,可以得到方程 ax+by=c,容易想到用拓展欧几里得求解
因为两个野人是不能在同一个洞里,要么就是方程无解,要么就是方程的解 x>min(Li,Lj)
因为 n<=15, 我们可以枚举 m>=max(Ci) 的情况,暴力判断野人 i,j 是否符合要求,这样子时间复杂度就是 O(n^2*m)

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int c[20],p[20],l[20],n,max1;
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
void exgcd(int a,int b,int &x,int &y){
    if(b==0){x=1,y=0;return;}
    exgcd(b,a%b,x,y);
    int k=x;
    x=y;y=k-a/b*y;}
bool judge(int m){
    int a,b,x,y,k,t;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++){
            a=p[i]-p[j],b=m,k=c[j]-c[i];
            t=gcd(a,b);
            if(k%t==0){
                a/=t;b/=t;k/=t;
                exgcd(a,b,x,y);
                b=abs(b);
                x=((k*x)%b+b)%b;
                if(x<=min(l[i],l[j])) return 0;}
        }
    return 1;}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&c[i],&p[i],&l[i]),max1=max(max1,c[i]);
    for(int i=max1;i;i++)
        if(judge(i)){
            cout<<i<<endl;
            return 0;}
}

猜你喜欢

转载自blog.csdn.net/chm_wt/article/details/81460911