分享奖品 c++

14 篇文章 0 订阅

分享奖品

题目描述
在学校创文知识竞赛中,明明和文文总共获得了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 !

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值