题解 P3010 【[USACO11JAN]分金Dividing the Gold】

· · 题解

此题一共有两个问题:

  1. 求金币分为两份的最小差值。
  2. 求分为最小差值时的方案数。

分步解答这两个问题。

第一个:

求两份金币的最小差值,那么我们很自然的想到,当两份金币相同时,差值最小。 那么题目就可以转化为,金币的价值最多能达到金币总价值的一半的多少。那么就是01背包了。

第二个:

直接求出第一问的金币价值的方案数即可。

下面贴下代码:


#include<bits/stdc++.h>
#define mod 1000000
using namespace std;
int n,a[350],tot,sum,f[500005];
long long dp[500005];
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read(),sum+=a[i];
    tot=sum;
    sum/=2;
    for(int i=1;i<=n;i++)
        for(int j=sum;j>=a[i];j--)
            f[j]=max(f[j],f[j-a[i]]+a[i]);
    printf("%d\n",tot-2*f[sum]);
    dp[0]=1;
    for(int i=1;i<=n;i++)
        for(int j=tot;j>=a[i];j--)
            dp[j]=(dp[j]+dp[j-a[i]])%mod;
    printf("%lld",dp[f[sum]]);
    return 0;
}