背包组题

完全背包

Dollars

UVa 147
给出100元、50元、20元、10元、5元、2元,1元、50分、20分、10分和5分共11种dollars,求组成所给价格的方法数。
每种dollar都有无限多,故为一个完全背包,先离线把表打出来,之后在线查询即可。
滚动数组实现。

#include <bits/stdc++.h>

#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N = 1e5 + 10;
int n, m;
ll dp[N],;
int b[]={1,2,4,10,20,40,100,200,400,1000,2000};
inline void init(){
    for(int i=0;i<=6000;i++) dp[i]=1;
    for(int i=1;i<11;i++)
        for(int j=b[i];j<=6000;j++)
             dp[j]+=dp[j-b[i]];
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    init();
    double money;
    while(cin>>money){
        if(money<1e-2) break;
        int n=money*20;
        printf("%6.2lf%17lld\n",money,dp[n]);
    }
    return 0;
}

Dollar Dayz

POJ 3181
1 − k 1-k 1k有无限多,求其凑出 n n n的方法数。
会爆 l o n g l o n g long long longlong,用大数,或者将超出的高位数单独记下,最后一起输出。

int main(){
    int n,k;
    cin >> n >> k;
    BigNum dp[1010];
    dp[0]=1;
    for (int i = 1; i <= k; i++) {
        for (int j = i; j <= n; j++) {
            dp[j] = dp[j] + dp[j-i];
        }
    }
    cout << dp[n] << endl;
    /*   
    ll dp[N][N];
    for (int i = 0; i <= n; i++) dp[i][0] = 1;
    for (int i = 1; i <= k; i++) {
    	if(j>=i) 
    		dp[i][j] = dp[i - 1][j] + dp[i][j - i];
    	else dp[i][j] = dp[i - 1][j];           
    }
    */
    return 0;
}

Piggy-Bank

POJ 1384
硬币无限,求容量下最小的价值。
完全背包。

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N = 1e4 + 10;
int v[N], w[N];
int dp[N];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T;
    cin >> T;
    while (T--) {
        int e, f, n, m;
        cin >> e >> f;
        m = f - e;
        cin >> n;
        for (int i = 1; i <= n; i++)
            cin >> v[i] >> w[i];
        memset(dp, inf, sizeof dp);
        dp[0] = 0;
        for (int i = 1; i <= n; i++)
            for (int j = w[i]; j <= m; j++)
                dp[j] = min(dp[j], dp[j - w[i]] + v[i]);
        if (dp[m] == inf)
            cout << "This is impossible.\n";
        else
            cout << "The minimum amount of money in the piggy-bank is " << dp[m] << ".\n";
    }
    return 0;
}

01背包

Charm Bracelet

POJ 3624
裸包,注意01背包和完全背包不同,需要倒序枚举容量即可。
滚动数组实现。

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N = 1e5 + 10;
int n, m;
int dp[N], w[N], v[N];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    while (scanf("%d%d",&n,&m)!=EOF) {
        for (int i = 0; i <= m; i++) dp[i] = 0;
        for (int i = 1; i <= n; i++)
            scanf("%d %d", &w[i], &v[i]);
        for (int i = 1; i <= n; i++)
            for (int j = m; j >= w[i]; j--)
                dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
        printf("%d\n", dp[m]);
    }
    return 0;
}

多重背包

Space Elevator

POJ 2392
n n n种不同类型的块,第 i i i类块的数量为 c i c_i ci,每个块的高度 h i h_i hi,允许这种类型的块达到的最高高度为 a i a_i ai,求将这些块组合能达到的最高高度。
a i a_i ai为关键字排序,这样就能使得塔的高度最大;
排序后按多重背包算法计算每一个塔高的可行性:设 d p [ k ] dp[k] dp[k]为建造高度 k k k的塔的可行性,则 d p [ k ] = d p [ k ] ∣ d p [ k − b l o c k s [ i ] . h i ] dp[k]=dp[k]|dp[k-blocks[i].hi] dp[k]=dp[k]dp[kblocks[i].hi]其中 0 ≤ i ≤ n , 1 ≤ j ≤ b l o c k s [ i ] . c i , b l o c k s [ i ] . a &lt; = k &lt; = b l o c k s [ i ] . h i 0≤i≤n,1≤j≤blocks[i].ci,blocks[i].a&lt;=k&lt;=blocks[i].hi 0in1jblocks[i].ciblocks[i].a<=k<=blocks[i].hi最后,按照可能塔高的递减顺序搜索 { d p n } \{dp_n\} {dpn},第一个dp[i]=1的塔高i即为塔的最大高度。

//贪心 + 可行性多重背包
#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N = 4e2 + 10;
typedef std::pair<int, int> pii;
int h[N], a[N], c[N];
struct node {
    int h, a, c;
    bool operator<(const node &rhs) const {
        return a < rhs.a;
    }
} blocks[N];
int dp[1 << 16], k;

int main() {
    while (cin >> k) {
        for (int i = 0; i < k; i++)
            scanf("%d %d %d", &blocks[i].h, &blocks[i].a, &blocks[i].c);
        sort(blocks, blocks + k);
        memset(dp, 0, sizeof(dp));
        dp[0] = 1;
        for (int i = 0; i < k; i++)
            for (int j = 0; j < blocks[i].c; j++)
                for (int z = blocks[i].a; z >= blocks[i].h; z--)
                    dp[z] |= dp[z - blocks[i].h];

        for (int i = blocks[k - 1].a; i >= 0; i--)
            if (dp[i]) {
                cout << i << endl;
                break;
            }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值