dp训练第27题 vijos1153 猫狗大战 背包

给定一个整数n(200)和n个数(40).
要求将这些数分组,使得个数最多差1且数字和最接近.

做法很多,这里列举一种
题意要求从n个数中选择n/2个,并使得和与总和的一半最为接近.
状态表示:可以用dp[i][j]表示选择了i个数时和为j的情况是否存在.
边界条件:dp[0][0]=1.
状态表示:dp[i][j]|=dp[i-1][j-save[k]]
复杂度(200*100*200*40)约为(2e8)???
最近几题的复杂度都这么凶险的吗

结果表示:
从dp[n/2][]中搜索,找到dp[n/2][j]为1且j距离sum/2最近的

代码是经典的三段dp:
1.读入/预处理
2.dp
3.输出结果

关于dp环节有几点:
1.需要倒转方向,因为经过了空间优化且每个物品只有一件
2.不用dp到0,因为本题中有个数i的限制.

最后的输出环节:
注意了,主要的复杂度都在dp上,所以dp部分以效率为主.
但是输出环节复杂度不会超过8000,所以应该以易读为主,不要想什么神奇操作,他让干什么就干什么就最好,比如本题中,直接选取两者差最小的就行.

const int N = 128, V = 8096;
int save[N];
int dp[N][V];

    int n=read(),sum=0;
    for(int i=1;i<=n;i++)
        sum+=(save[i]=read());

    dp[0][0]=1;
    for(int k=1;k<=n;k++)
        for(int i=min(n/2,k);i>=1;i--)
            for(int j=min(k*40,sum);j>=save[k];j--)
                dp[i][j]|=dp[i-1][j-save[k]];

    int ans=0;
    for(int j=0;j<=sum;j++)
        if(dp[n/2][j]&&abs(j*2-sum)<abs(ans*2-sum))
            ans=min(j,sum-j);
    printf("%d %d\n",ans,sum-ans );
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值