P13013 [GESP202506 五级] 奖品兑换 题解

· · 题解

由于通过 n \gets n-a,m \gets m-bn \gets n-b,m \gets m-a 两种方式均可兑换奖品,所以 n,m 的顺序显然是可以互换的。

暴力

显然,我们可以写出时间复杂度为 O(n) 的暴力。

大致逻辑为:每次从两种券中选择数量较多的一种,花费较多的这种券来兑换奖品。

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int P=1e9+7;
const int N=1e5+10;
int n,m,a,b,ans;

signed main()
{
    cin>>n>>m;
    cin>>a>>b;
    if(a<b)swap(a,b);  //确保 a 较大
    while(n>=a||m>=a)
    {
        if(n<m)swap(n,m);
        if(m<b)break;    
        n-=a;
        m-=b;
        ans++;  //从n,m中选择较大的变量减去较大的花费
    }
    cout<<ans<<endl;
    return 0;
}

正解

注:下文中默认 a \ge b,n \ge m

由于每次我们都让较大的变量减少的更多,所以在进行0次或多次兑换后,$n,m$ 两变量的差值一定会使下次操作后 $n < m$。 具体地,令 $d=a-b,v= \min(n\div a,m\div b,(n-m)\div d)$,则在进行 $v$ 次兑换后,一定会有 $n-m<d$ 或不可再进行兑换。 当 $n-m < d$ 时,下次操作一定会使 $n < m$ 且 $m-n < d$,于是再下次操作又会使 $n > m$,以此类推。 并且在进行两次操作后,由于 $n,m$ 均减去了 $a+b$,操作后的 $n-m$ 与开始的 $n-m$ 大小相等。 因此,当 $n-m < d$ 时,可以令 $y=m \div (a+b)$,进行 $y \times 2 $ 次操作,具体看代码。 ### 代码 ```cpp #include <bits/stdc++.h> using namespace std; #define int long long const int P=1e9+7; const int N=1e5+10; int n,m,a,b,ans; int s,d; signed main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin>>n>>m; cin>>a>>b; if(a<b)swap(a,b); if(a==b) //进行特判 { cout<<min(n,m)/a<<endl; return 0; } s=a+b,d=a-b; while(n>=a||m>=a) { if(n<m)swap(n,m); int x=(n-m)/d; if(x==0) //当n-m<d时 { int y=m/s; ans+=y*2; n-=y*s,m-=y*s; x=2; } int v=min(n/a,min(m/b,x)); ans+=v,n-=v*a,m-=v*b; if(m<b)break; //不可进行兑换 } cout<<ans<<endl; return 0; } ``` 可以证明,while 循环最多执行**两次**(最开始执行一次使 $n-m<d$,第二次就可兑换完)。 因此,此代码时间复杂度近似 $O(1)