分享奖品
题目描述
在学校创文知识竞赛中,明明和文文总共获得了n(1≦ n≦250)件奖品,每件奖品都有一个价值Vi (1 ≦ Vi ≦ 2,000)。他们想按价值平均分享这些奖品,假如不能平均分就尽量让它们的差距最小。现在给出奖品数及它们的价值,明明想算出划分后的最小差值,以及划分的方案数。
例如:有5件奖品价值分别是:2, 1,8, 4, 16。明明和文文分为两部分,分别是前面四个为一部分1+2+4+8=15,16为单独一部分,那么两部分相差:16-15 = 1。这个是差距最小的划分方案,并且这种方案的划分方法只有1种。
相同价值的奖品相交换算不同的方案,如:有四件奖品价值分别为{1, 1, 1, 1},有6种不同的划分方案,使这些奖品分为两部分,每一部分2个奖品。
输入
第一行:一个整数n(1≦n≦250);
接着有n行,每行一个整数Vi (1 ≦ Vi ≦ 2,000)代表每件奖品的价值。
输出
第一行:一个整数,代表划分的两部分的最小差值。
第二行:一个整数,代表最小差值的划分方案数,结果对1,000,000求余(mod 1,000,000)。
样例输入 #1:
5
2
1
8
4
16
样例输出 #1:
1
1
样例输入 #2:
4
1
1
1
1
样例输出 #2:
0
6
思路
首先可以用动态规划的思想:
设 f[i][j] 为前i件奖品他们价值为j的不同方案
那么状态转移方程就是:
f[i][j] = f[i-1][j]+f[i-1][j-a[i]]
这时写的代码是这样的:
#include<bits/stdc++.h>
using namespace std;
const int MOD = 1000000;
const int N = 300;
int n,a[N],f[N][2000*N];
int maxn = 0;
int main(){
cin >> n;
for(int i=1;i<=n;i++)cin >> a[i],maxn+=a[i];
f[0][0] = 1;
for(int i=1;i<=n;i++){
for(int j=0;j<=maxn;j++){
f[i][j] = (f[i-1][j]+f[i-1][j-a[i]])%MOD;
}
}
int ans=2000*N,cnt=-1;
for(int j=0;j<=maxn;j++){
if(abs((maxn-j)-j)<ans){
ans = abs((maxn-j)-j);
cnt = f[n][j];
}
}
cout << ans << endl << cnt << endl;
return 0;
}
结果: MLE
这个代码的空间复杂度太高了。于是我把f的第一维改成了滚动数组:
#include<bits/stdc++.h>
using namespace std;
const int MOD = 1000000;
const int N = 260;
int n,a[N],f[3][2000*N];
bool vis[3][2000*N];
int maxn = 0;
int main(){
cin >> n;
for(int i=1;i<=n;i++)cin >> a[i],maxn+=a[i];
f[0][0] = 1;
vis[0][0] = 1;
for(int i=1;i<=n;i++){
for(int j=0;j<=maxn;j++){
if(j-a[i]>=0){
f[i%2][j] = (f[(i-1)%2][j]+f[(i-1)%2][j-a[i]])%MOD;
vis[i%2][j]= vis[(i-1)%2][j] || vis[(i-1)%2][j-a[i]];
}
else{
f[i%2][j] = f[(i-1)%2][j];
vis[i%2][j] = vis[(i-1)%2][j];
}
}
}
int ans=2000*N,cnt=-1;
for(int j=0;j<=maxn;j++){
if(abs((maxn-j)-j)<ans and vis[n%2][j]!=0){
ans = abs((maxn-j)-j);
cnt = f[n%2][j];
}
}
cout << ans << endl << cnt << endl;
return 0;
}
这次又变成了TLE
再向时间优化,将 mod 的次数变少然后把vis数组加上(就是记录能否走到这个点的数组)最终的答案是:
#include<bits/stdc++.h>
using namespace std;
const int MOD = 1000000;
const int N = 260;
int n,a[N],sum[N],f[3][2000*N];
bool vis[3][2000*N];
int maxn = 0;
int main(){
cin >> n;
for(int i=1;i<=n;i++){
cin >> a[i];
maxn+=a[i];
sum[i]=sum[i-1]+a[i];
}
f[0][0] = 1;
vis[0][0] = 1;
for(int i=1;i<=n;i++){
int I2=i%2,I12=(i-1)%2;
for(int j=0;j<=sum[i];j++){
if(j-a[i]>=0){
f[I2][j] = f[I12][j]+f[I12][j-a[i]];
if(f[I2][j]>=MOD) f[I2][j]-=MOD;
vis[I2][j]= vis[I12][j] || vis[I12][j-a[i]];
}
else{
f[I2][j] = f[I12][j];
vis[I2][j] = vis[I12][j];
}
}
}
int ans=2000*N,cnt=-1;
for(int j=0;j<=maxn;j++){
if(abs((maxn-j)-j)<ans and vis[n%2][j]!=0){
ans = abs((maxn-j)-j);
cnt = f[n%2][j];
}
}
cout << ans << endl << cnt << endl;
return 0;
}
变成了 AC !