总的思路:贪心
因为题目中说明了w>d,那么我们可以让y取得最小非负解,那么就可以使得x+y更小,这样就使z有解的可能性最大。
第一种解法:暴力
根据拓展欧几里得的x,y的通解。可知y的最小非负解的取值范围是0<=y<w,题中w=1e5。那么就可以直接暴力0~w的所有值,看是否满足条件。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,p,w,d;
int main()
{
while(scanf("%lld%lld%lld%lld",&n,&p,&w,&d)!=EOF)
{
bool f=0;
for(ll i=0;i<w;i++)
{
if((p-i*d)%w==0)
{
ll t=(p-i*d)/w;
if(t+i<=n&&t>=0&&i>=0)
{
f=1;
printf("%lld %lld %lld\n",t,i,n-t-i);
break;
}
}
}
if(!f)
puts("-1");
}
return 0;
}
第二种解法:拓展欧几里得
如果直接用拓展欧几里得求解,因为题目中的p的取值为1e17。很容易就会超long long ,因此不能采用常规解法。
可以先求出w 和 d的最大公约数 val,先用p%val 判断方程是否有解。
如果有解,进行下一步操作:
w/=val;
d/=val;
p/=val;
把方程的系数和常数项缩小,保证解不变。
然后解此时方程w * x + d * y =p。
首先先用拓展欧几里得解方程:wx’+dy’=1。
求出此方程的 y’ 的最小非负解。
那么对于这两个方程:
1.wx’+dy’=1;
2.wx+dy=p;
基于贪心的思想,让x尽可能大,y尽可能小。把p尽可能分配给x,那么应该把p%w给y。
方程1的解要转化为方程2的解,应该要乘以一个p,但这样就不能保证y为方程最小的非负解。但可以发现,方程1左右乘以p后,y’乘上了p后,由于p可以用w和d表示,因此乘p后的y’,可以分解出一部分归入wx项中,那么就相当于直接用p%w去乘以y’,把y’转化为方程2的解。通过y的最小非负解,就可以求出x和z,只要判断满不满足条件即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,p,w,d;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
ll ans=exgcd(b,a%b,x,y);
ll tmp=x;
x=y;
y=tmp-(a/b)*y;
return ans;
}
ll cal(ll a,ll b,ll c)
{
ll x,y;
ll gcd=exgcd(a,b,x,y);
if(c%gcd)
return -1;
x=(x%b+b)%b;
return x;
}
int main()
{
while(scanf("%lld%lld%lld%lld",&n,&p,&w,&d)!=EOF)
{
ll val=__gcd(w,d);
if(p%val)
{
puts("-1");
continue;
}
d/=val;
w/=val;
p/=val;
ll a=cal(d,w,1);
ll yy=a*(p%w)%w;
ll xx=(p-yy*d)/w;
ll zz=n-xx-yy;
if(zz<0||xx<0)
puts("-1");
else
printf("%lld %lld %lld\n",xx,yy,zz);
}
return 0;
}