POJ Comfort (扩欧的应用)
题意: n个点顺时针围成一圈(编号1~n),初始时在1点,每次可以顺时针移动k点,n个点中有一些点有障碍物,求最小的k使得在不经过任何一个有障碍物的点就可以到达z点
其实就是求 一元线性同余方程。
具体做法是枚举k 找到最小的k 让 kx=z%n (kx+ny=z) 然后再考虑 是否经过障碍点。
在找到了 一个k 的解P之后我们可以枚举 障碍物的位置再求一遍,如果出现了整数解x并且 x<P;
这就说明了会经过障碍物点( 不知道原因的想一下 求出的整数解代表 的含义)
为了求余方便可以把所有的点-1;
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int a[1000];
int ex_gcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
int d=ex_gcd(b,a%b,y,x);
y-=x*(a/b);
return d;
}
int solve(int a,int b,int c)
{
int x, y;
int d=ex_gcd(a,b,x,y);
if(c%d!=0) return -1;
x=x*c/d;
int t=b/d;
x=(x%t+t)%t;
return x;
}
int main()
{
int n,z,m,k;
while(scanf("%d%d%d",&n,&z,&m)!=EOF)
{
z--;
for(int i=0;i<m;i++)
scanf("%d",&a[i]),a[i]--;
for(k=1;k<=z;k++)
{
int cnt=solve(k,n,z);
if(cnt==-1) continue;
int flag=1;
for(int i=0;i<m;i++)
{
int x=solve(k,n,a[i]);
if(x!=-1&&x<cnt) flag=0;
}
if(flag) break;
}
printf("%d\n",k);
}
return 0;
}
一元线性同余式 的计算模板:
int ex_gcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
int d=ex_gcd(b,a%b,y,x);
y-=x*(a/b);
return d;
}
int solve(int a,int b,int c)
{
int x, y;
int d=ex_gcd(a,b,x,y);
if(c%d!=0) return -1;
x=x*c/d;
int t=b/d;
x=(x%t+t)%t;
return x;
}