vijosp1037-类背包问题&好题-搭建双塔

https://vijos.org/p/1037
问你用以下的数字构建两个 相同高度的双塔,尽可能的高。
开始的思路是 背包计数。
如果 dp[m] 和dp[m/2] 和m%2==0 都满足,那么我们就可以输出m/2
(m和 m/2 这些数字都能拼出来。。)
但是即使这样,其实也不一定是可以的,因为很可能 m/2构成的两次使用了重叠的项,而我们并没有阻止这一种情况

// 这时错误的代码。过了不到一半的数据qwq
#include <bits/stdc++.h>
using namespace std;
const int maxn=3000;
int a[maxn];
int dp[maxn];
int m;
int main()
{    scanf("%d",&m);
     int sum=0;
     for(int i=0;i<m;i++){
         scanf("%d",&a[i]);
         sum+=a[i];
     }
     memset(dp,0,sizeof(dp));
     dp[0]=1;
     for(int i=0;i<m;i++){
        for(int j=sum;j>=a[i];j--)
            dp[j]+=dp[j-a[i]];
     }
     int x=sum;
     while(1){
        if(dp[x]&&x%2==0&&dp[x/2]>=2){
        printf("%d\n",x/2);
        break;
        }
        else
            x--;
     }


    return 0;
}

正解是 类似背包的dp
dp[i][j] 表示使用了1-i个数字,两个塔差值为j时候 矮塔的个数。

#include <bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=3000;
int a[maxn];
int dp[2][maxn];
int m;
int main()
{    scanf("%d",&m);
     int sum=0;
     for(int i=1;i<=m;i++){
         scanf("%d",&a[i]);
         sum+=a[i];
     }
    for(int i=0;i<2;i++){
        for(int j=1;j<maxn;j++)
            dp[i][j]=-inf;
    }
     for(int i=1;i<=m;i++){
        for(int j=0;j<=sum;j++){
            dp[i%2][j]=max(dp[i%2][j],dp[(i-1)%2][j]);//不使用 当前第i个数字
            if(j>=a[i])
            dp[i%2][j]=max(dp[i%2][j],dp[(i-1)%2][j-a[i]]);
            // 将这个数字放在高塔上。(这点注意思考)
            if(j+a[i]<=sum)
            dp[i%2][j]=max(dp[i%2][j],dp[(i-1)%2][j+a[i]]+a[i]);// 放在矮塔上。大小并未改变。
            if(j<a[i])
            dp[i%2][j]=max(dp[i%2][j],dp[(i-1)%2][a[i]-j]+a[i]-j);//放在矮塔上,埃塔变成了高塔。(这个数字大于差值。改变了
            )

        }
     }
     bool flag=false;
     if(dp[(m)%2][0]){
        printf("%d\n",dp[(m)%2][0]);
         flag=true;
         }

     if(!flag)
        puts("Impossible");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值