基础背包问题

这篇博客探讨了如何解决等和子集的划分问题,介绍了电子科大食堂饭卡的奇异设计,并提出了一个寻找最小余额的策略。此外,还讨论了蒜头君申请学校时面临的费用与概率问题,如何最大化获得offer的概率。通过代码实例展示了问题的解决方案。
摘要由CSDN通过智能技术生成

等和的分隔子集

晓萌希望将 1 到 N 的连续整数组成的集合划分成两个子集合,且保证每个集合的数字和是相等。例如,对于 N = 3,对应的集合 {1, 2, 3} 能被划分成 {3} 和{1, 2} 两个子集合。
这两个子集合中元素分别的和是相等的。
对于N=3,我们只有一种划分方法,而对于N=7 时,我们将有 4 种划分的方案。

输入格式
输入包括一行,仅一个整数,表示 N 的值 (1≤N≤39)。

输出格式
输出包括一行,仅一个整数,晓萌可以划分对应 N 的集合的方案的个数。当没发划分时,输出 0。

样例输入
7
样例输出
4
代码

#include<bits/stdc++.h>
using namespace std;
long long  dp[50][50005];
int n,ans;
int main() {
    cin>>n;
    int s=(1+n)*n/2;
    if(s & 1) {
        cout<<0<<endl;
        return 0;
    } 
    //s & 1表示s是否为奇数,如果是奇数就肯定不能一分为二,
    int ss=s/2;
    dp[0][0]=1;
    
    for(int i=1;i<ss;i++) {
        dp[0][i]=0;//当背包容量为i,前零个数的方案为1
    }
    for(int i=1;i<=n;i++) {
        for(int h=0;h<=ss;h++) {
            if(h<i) dp[i][h]=dp[i-1][h];//当h<i时,意味着背包需求为h,但是我i已经大于h了,意味着肯定不能把i放进背包,所以当前情况为上一种情况
            else {
                dp[i][h]=dp[i-1][h]+dp[i-1][h-i];
                //要么放进去要么不放进去
            }
        }
        
    }
    cout<<dp[n][ss]/2<<endl;//除以二是有重复的
    return 0;
}

其他代码求解

#include<bits/stdc++.h>
using namespace std;
long long  dp[2][50005];
int n,sum;
int main() {
    cin>>n;
    sum=n*(n+1)/2;
    dp[0][0]=1;
    if(sum%2==1) {
        cout<<0<<endl;
        return 0;
    }
    for(int i=1;i<=n;i++) {
       for(int j=0;j<=sum/2;j++) {
           dp[i & 1][j]=dp[!(i & 1)][j]; //把上一个状态全抄过来
       }
        for(int j=i;j<=sum/2;j++) {
            dp[i & 1][j]=dp[!(i & 1)][j]+dp[!(i & 1)][j-i];//j先从i开始,因为我们只要到sum/2就可以
        }
    }
    cout<<dp[n & 1][sum/2]/2<<endl;
    return 0;
}

饭卡

电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于 5 元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有 n 种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。

输入格式
第一行为正整数 n(n≤1000) ,表示菜的数量。
第二行包括 n个正整数,表示每种菜的价格。价格不超过 5050。
第三行包括一个正整数 m(m≤1000),表示卡上的余额。

输出格式
对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。
样例输入1
1
50
5
样例输出1
-45
样例输入2
10
1 2 3 2 1 1 2 3 2 1
50
样例输出2
32
代码

/**先对菜价进行排序,然后选出菜价最大的,从剩下的菜中选出x个使得总价格最接近
m-5,最后让m减去这个菜价和再减去菜价最大的那个即为最小余额**/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int m,n;
int a[1101];
int dp[1101];
int solve(){
    int i,j;
    for(i = 0; i < n-1; i++){
        for(j = m-5; j >= a[i]; j--){
            dp[j] = max(dp[j],dp[j-a[i]]+a[i]);
        }
    }
    return dp[m-5];
}
int main(){
    int i;
    while(cin >> n){
        if(!n)break;
        int i;
        for(i = 0; i < n; i++)
            cin >> a[i];
        cin >> m;
        sort(a,a+n);
        memset(dp,0,sizeof(dp));
        int cc = solve();
        if(m<5)cout << m << endl;
        else cout << m-cc-a[n-1] << endl;
    }
    return 0;
}

感觉有上个代码有点不舒服,这个是自己又写了一遍

#include<bits/stdc++.h>
using namespace std;
long long  dp[2][50005];
int n,sum,a[1111],m;
int main() {
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    cin>>m;
    if(m<5) {
        cout<<m<<endl;
        return 0;
    }
    sort(a+1,a+1+n);
    dp[0][0]=0;
    for(int i=1;i<n;i++) {
       for(int j=0;j<=m-5;j++) {
           dp[i & 1][j]=dp[!(i & 1)][j]; 
       }
        for(int j=a[i];j<=m-5;j++) {
            dp[i & 1][j]=max(dp[!(i & 1)][j],dp[!(i & 1)][j-a[i]]+a[i]);
        }
    }
    cout<<m-dp[(n-1) & 1][m-5]-a[n]<<endl;
    return 0;
}

还有一种代码是别人写的,我感觉可以在里面学点东西,这里也写出来了

#include<bits/stdc++.h>
using namespace std;
long long  dp[2][50005];
int n,mx,a[1111],m;
int main() {
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    cin>>m;
    if(m<5) {
        cout<<m<<endl;
        return 0;
    }
    sort(a+1,a+1+n);
    dp[0][0]=1;
    for(int i=1;i<n;i++) {
       for(int j=0;j<=m-5;j++) {
           dp[i & 1][j]=dp[!(i & 1)][j]; 
       }
        for(int j=a[i];j<=m-5;j++) {
          if(dp[!(i & 1)][j-a[i]]) {
              dp[i & 1][j]=1;//对可行性来做
          }
        }
    }
    for(int i=m-5;i>=0;i--) {
        if(dp[(n-1) &  1][i])
        {
            mx=max(mx,i);//一旦找到便直接break
            break;
        }
    }
    cout<<m-mx-a[n]<<endl;
    return 0;
}

offer

蒜头君很早就想出国,现在他已经考完了所有需要的考试,准备了所有要准备的材料,于是,便需要去申请学校了。要申请国外的任何大学,你都要交纳一定的申请费用,这可是很惊人的。蒜头君没有多少钱,总共只攒了 n 万美元。他将在 m 个学校中选择若干的(当然要在他的经济承受范围内)。每个学校都有不同的申请费用 a(万美元),并且蒜头君估计了他得到这个学校 offer 的可能性 b。
不同学校之间是否得到 offer 不会互相影响。“I NEED A OFFER”,他大叫一声。帮帮这个可怜的人吧,帮助他计算一下,他可以收到至少一份offer的最大概率。(如果蒜头君选择了多个学校,得到任意一个学校的offer都可以)。
输入格式
第一行有两个正整数 n,m((0≤n≤10000,0≤m≤10000)。
后面的 m 行,每行都有两个数据ai(整形) bi(实型)
分别表示第 i 个学校的申请费用和可能拿到 offer 的概率。
输出格式
每组数据都对应一个输出,表示蒜头君可能得到至少一份 offer 的最大概率。用百分数表示,精确到小数点后一位。
样例输入
10 3
4 0.1
4 0.2
5 0.3
样例输出
44.0%
重点是概率的求法,
题目链接

#include<bits/stdc++.h>
using namespace std;
int a;
double b,dp[2][10005];
int main() {
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++) {
       cin>>a>>b;
       for(int j=0;j<=n;j++) 
       {
           dp[i & 1][j] = dp[!(i & 1)][j];//把上一轮的情况全部抄过来
       }
       for(int j=a;j<=n;j++) {
           dp[i & 1][j]=max(dp[i & 1][j],1.0-(1-dp[!(i & 1)][j-a])*(1-b));
           //至少一份 offer 的最大概率==1.0-(这次没有得到的概率)*上次也没有得到的概率
       }
    }
    cout<<fixed<<setprecision(1)<<dp[m & 1][n]*100<<"%"<<endl;
    //cout<<fixed<<setprecision(x)<<a<<endl;
    //意思是保留a小数点后的x位小数,注意fixed和setprecision是要连起来用的
    //如果用printf写的话要这样写
    //printf("%.1lf%%\n",dp[m&1][n]*100);
    //在printf中%是有意义的。
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值