Codevs 1959 拔河比赛

本文针对一个具体的背包问题进行解析,通过两种不同的方法实现——一种是简单的贪心算法仅获得70分,另一种则是使用动态规划达到了完全正确的解答。文中详细介绍了这两种方法的实现思路与代码实现,同时分析了贪心算法的局限性和动态规划的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

http://codevs.cn/problem/1959/

题解:

乱搞70:数据水
1.把对应质量赋价值,sum/2最高,向两边递减。
体积为人数,做01。只得70
2.0~sum/2递增赋值,大于sum/2的赋值为0;
cnt=n/2,以cnt和n-cnt为体积分别做01(防止某一组的总重量大于sum/2),取差值小的那组答案输出。也得70
WA的原因
有后效性,当前的选择影响了以后的选择能不能达到最优值
质量和所赋的价值并不是完全成正比的。不是“价值”越高越好
对于01 相同体积内,得到的物品价值和越大越优,无后效型;
对于本题 相同人数,并不是质量越接近sum/2越优,某个更接近sum/2的质量有可能阻碍了以后的最优值选择。
譬如 sum/2=70 现在有质量为20和30两种选择,因为上述赋值,肯定会选择30,
但是下一层循环若有一个质量为50的人,本来可以和20组合,达到最优值,但是由于刚刚的目光短浅选择了30,错过了最优值。

正解DP:(抄题解)
dp[i][j][k]=1/0 表示从前i个人中选了j个人,能否达到k重量

这个状态有一点像
http://blog.youkuaiyun.com/loi_lxt/article/details/78124820

dp[i][j][k]=dp[i-1][j][k]|dp[i-1][j-1][k-w[i]];
                不选第i个人    选第i个人

最后枚举可能的k值,记录答案;
注意:
二维数组某一维是负数,,,不re,,,wa

数组越界!!!!!

代码:

70分乱搞
#include<iostream>
#include<cstdio>
using namespace std;
const int N=100+60;
int n,w[N],dp[N],sum,cnt,mp[55000];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]),sum+=w[i];
    cnt=n/2;

    for(int i=1;i<=sum/2;i++) mp[i]=i*3,mp[sum-i]=mp[i];
    //for(int i=1;i<=sum;i++) printf("%d ",mp[i]);
    for(int i=1;i<=n;i++){
        for(int j=cnt;j>=1;j--){
            if(mp[dp[j]]<mp[dp[j-1]+w[i]]) dp[j]=dp[j-1]+w[i];
        }
    }
    int ans1=dp[cnt];
    int ans2=sum-dp[cnt];
    if(ans1>ans2) swap(ans1,ans2);
    cout<<ans1<<" "<<ans2<<endl;
    return 0;
}
AC DP
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int N=100+2;
int n;
int w[N],dp[N][N*451],sum,ans,minn=1e9+7;;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]),sum+=w[i];
    dp[0][0]=1;
    int cnt=(n+1)/2;
    for(int i=1;i<=n;i++)
    for(int j=cnt;j>=1;j--)
    for(int k=sum;k>=w[i];k--){
        dp[j][k]=dp[j][k]|dp[j-1][k-w[i]];
    }

    for(int k=0;k<=sum;k++){
        if(!dp[cnt][k]) continue;
        int tmp=k;
        if(abs(sum-tmp-tmp)<minn){
            minn=abs(sum-tmp-tmp);
            ans=tmp;
        }
    }
    int ans2=sum-ans;
    if(ans>ans2) swap(ans,ans2);
    printf("%d %d",ans,ans2);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值