动态规划之背包模型例题

1. HDU 1203 I NEED A OFFER!

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1203

稍微转化下,dp[i]表示的是前i个学校不被接受的最小概率。

代码如下:

//include <bits/stdc++.h>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <time.h>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll MOD =  10007;
const int maxn = 1e4+5;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
template <class T>
inline bool scan_d(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
int n,m;
double dp[maxn];
int v[maxn];
double g[maxn];
double ans=0;
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0) break;
        for (int i=1;i<=m;i++)
        {
            scanf("%d%lf",&v[i],&g[i]);
        }
        dp[0]=1;
        for (int i=0;i<=n;i++) dp[i]=1;
        for (int i=1;i<=m;i++)
        {
            for (int j=n;j>=v[i];j--)
            {
                dp[j]=min(dp[j],dp[j-v[i]]*(1-g[i]));
            }
        }
        double ans=0;
        for (int i=0;i<=n;i++)
        {
            ans=max(ans,(1-dp[i])*100);
        }
        printf("%.1f%%\n",ans);
    }
	return 0;
}

2.  HDU  2546 饭卡

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=2546

先将m减去5,选物品中的最大值,然后dp

代码如下:

//include <bits/stdc++.h>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <time.h>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll MOD =  10007;
const int maxn = 1e3+5;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
template <class T>
inline bool scan_d(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
int n,m;
int v[maxn];
int dp[maxn];
int main()
{
    while(scanf("%d",&n)&&n)
    {
        int Max=-1,loc=0;
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&v[i]);
            if(Max<v[i])
            {
                Max=v[i];
                loc=i;
            }
        }
        scanf("%d",&m);
        m-=5;
        memset (dp,0,sizeof(dp));
        for (int i=1;i<=n;i++)
        {
            if(i==loc) continue;
            for (int j=m;j>=v[i];j--)
            {
                dp[j]=max(dp[j],dp[j-v[i]]+v[i]);
            }
        }
        int ans=0;
        for (int i=0;i<=m;i++)
        {
            dp[i]+=Max;
            ans=max(ans,dp[i]);
        }
        printf("%d\n",m+5-ans);
    }
	return 0;
}

3. HDU 2602 Bone Collector

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=2602

裸的01背包。

代码如下:

//include <bits/stdc++.h>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <time.h>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll MOD =  10007;
const int maxn = 1e3+5;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
template <class T>
inline bool scan_d(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
int t;
int n,v;
int a[maxn];
int b[maxn];
int dp[maxn];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&v);
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&b[i]);
        }
        memset (dp,0,sizeof(dp));
        int ans=0;
        for (int i=1;i<=n;i++)
        {
            for (int j=v;j>=b[i];j--)
            {
                dp[j]=max(dp[j],dp[j-b[i]]+a[i]);
                ans=max(ans,dp[j]);
            }
        }
        printf("%d\n",ans);
    }
	return 0;
}

4. HDU 1864 最大报销额

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1864

因为输入的金额有小数,所以需要进行扩大位数,将每个金额*100,变成整数的形式然后进行01背包dp。

注意此题如果出现别的类型的发票,按题目要求这种发票不进行处理,并且类型A,B,C中的每个金额总和大于600也是不行的,还有A+B+C的总额大于1000也是不符合要求的。

因为只有30个发票,而且每个发票的金额不超过1000,然后再进行转化为整数操作,所以需要开30*1000*100+50的dp数组。

还有dp的时候不能100,100的递减,需要处理到每一位小数,所以需要1,1的处理。

复杂度为9*1e7;

代码如下:

//include <bits/stdc++.h>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <time.h>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll MOD =  10007;
const int maxn = 3*1e6+50;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
template <class T>
inline bool scan_d(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
double q;
int n,m;
int dp[maxn];
int a[35];
int main()
{
    while(scanf("%lf%d",&q,&n)!=EOF)
    {
        if(n==0) break;
        int qq=(int)(q*100);
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&m);
            getchar();
            int sum=0;
            int ok=1;
            int aa=0,bb=0,cc=0;
            while(m--)
            {
                char c;
                double x;
                scanf("%c:%lf",&c,&x);
                getchar();
                int t=(int)(x*100);
                if(c=='A')
                {
                    aa+=t;
                }
                else if(c=='B')
                {
                    bb+=t;
                }
                else if(c=='C')
                {
                    cc+=t;
                }
                else
                {
                    ok=0;
                }
            }
            if(ok&&aa+bb+cc<=100000&&aa<=60000&&bb<=60000&&cc<=60000)
            {
                a[i]=aa+bb+cc;
            }
            else
            {
                a[i]=0;
            }
        }
        memset (dp,0,sizeof(dp));
        int ans=0;
        for (int i=1;i<=n;i++)
        {
            for (int j=qq;j>=a[i];j--)
            {
                dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
                ans=max(ans,dp[j]);
            }
        }
        printf("%.2lf\n",ans/100.0);
    }
	return 0;
}

5. POJ 1293 Duty Free Shop

题目链接:

http://poj.org/problem?id=1293

利用01背包求出最大包装的巧克力数量,并记录下dp路径,然后判断剩下的巧克力数量是否小于另一种巧克力的数量。

代码如下:

//include <bits/stdc++.h>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <time.h>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll MOD =  10007;
const int maxn = 1e3+5;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
template <class T>
inline bool scan_d(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
int dp[maxn];
int m,l;
int n;
int a[maxn];
int path[maxn];
int vis[maxn];
bool can ()
{
    memset (dp,-1,sizeof(dp));
    memset (path,-1,sizeof(path));
    memset (vis,0,sizeof(vis));
    dp[0]=0;
    for (int i=1;i<=n;i++)
    {
        for (int j=m;j>=a[i];j--)
        {
            if(dp[j]<dp[j-a[i]]+a[i]&&dp[j-a[i]]!=-1)
            {
                dp[j]=dp[j-a[i]]+a[i];
                path[j]=i;
            }
        }
    }
    vector<int>ans;
    int q=m;
    while(dp[q]==-1) q--;
    while(q>0)
    {
        ans.push_back(path[q]);
        vis[path[q]]=1;
        q=dp[q]-a[path[q]];
    }
    int sum=0;
    for (int i=1;i<=n;i++)
    {
        if(vis[i]==0) sum+=a[i];
    }
    if(sum>l) return false;
    printf("%d ",ans.size());
    sort(ans.begin(),ans.end());
    for (int i=0;i<ans.size();i++)
    {
        printf("%d ",ans[i]);
    }
    printf("\n");
    return true;
}
int main()
{
    while(scanf("%d%d",&m,&l)!=EOF)
    {
        if(m==0&&l==0) break;
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        int ok;
        ok=can();
        if(ok) continue;
        printf("Impossible to distribute\n");
    }
	return 0;
}

6. HDU 2955 Robberies

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=2955

一开始看错题了,以为是概率相加,然后还将概率作为01背包的dp对象,然后概率*100,跑出的样例怎么也不对,发现会出现精度问题,看了disscuss发现可以把钱数看成dp对象,这个范围就是钱数总和,依然还是没有读对题。

之后看别人的题解,发现给出的是被抓的概率,所以求解不被抓的概率需要用1减去,而且概率之间是需要相乘,不能相加。

稍微改一下dp方程,然后dp完后倒着找第一个大于等于1减去p的。

代码如下:

//include <bits/stdc++.h>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <time.h>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll MOD =  10007;
const int maxn = 1e4+5;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
template <class T>
inline bool scan_d(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
int t;
int n;
double v;
double dp[maxn];
int a[maxn];
double b[maxn];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lf%d",&v,&n);
        memset (dp,0,sizeof(dp));
        dp[0]=1;
        int sum=0;
        for (int i=1;i<=n;i++)
        {
            scanf("%d%lf",&a[i],&b[i]);
            sum+=a[i];
        }
        for (int i=1;i<=n;i++)
        {
            for (int j=sum;j>=a[i];j--)
            {
                if(dp[j]<dp[j-a[i]]*(1-b[i]))
                {
                    dp[j]=dp[j-a[i]]*(1-b[i]);
                }
            }
        }
        int ans=0;
        double ansp=0;
        for (int i=sum;i>=1;i--)
        {
            if(dp[i]>(1-v))
            {
                ans=i;
                break;
            }
        }
        printf("%d\n",ans);
    }
	return 0;
}

7. HDU 1114 Piggy-Bank 

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1114

完全背包模板题。

代码如下:

//include <bits/stdc++.h>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <time.h>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll MOD =  10007;
const int maxn = 1e5+5;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
template <class T>
inline bool scan_d(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
int t,n;
int dp[maxn];
int a[maxn];
int b[maxn];
int kong,man;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&kong,&man);
        int v=man-kong;
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            scanf("%d%d",&a[i],&b[i]);
        }
        for (int i=1;i<=v;i++)
        {
            dp[i]=INF;
        }
        dp[0]=0;
        for (int i=1;i<=n;i++)
        {
            for (int j=0;j<=v;j++)
            {
                if(b[i]<=j)
                {
                    dp[j]=min(dp[j],dp[j-b[i]]+a[i]);
//                    printf("dp[%d]=%d dp[%d]=%d\n",j,dp[j],j-b[i],dp[j-b[i]]);
                }
            }
        }
        if(dp[v]>=INF)
        {
            printf("This is impossible.\n");
        }
        else
        {
            printf("The minimum amount of money in the piggy-bank is %d.\n",dp[v]);
        }
    }
	return 0;
}

8.  POJ 1276 Cash Machine  多重背包模板

题目链接:

http://poj.org/problem?id=1276

代码如下:

//include <bits/stdc++.h>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <time.h>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll MOD =  10007;
const int maxn = 1e5+5;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
template <class T>
inline bool scan_d(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
int dp[maxn];
int n;
int v;
int num[15];
int w[15];
void zero (int w)
{
    for (int i=v;i>=w;i--)
        dp[i]=max(dp[i],dp[i-w]+w);
}
void unlimit (int w)
{
    for (int i=w;i<=v;i++)
        dp[i]=max(dp[i],dp[i-w]+w);
}
void mul ()
{
    for (int i=1;i<=n;i++)
    {
        if(num[i]*w[i]>v)
        {
            unlimit(w[i]);
        }
        else
        {
            int k=1;
            while(num[i]>k)
            {
                zero (k*w[i]);
                num[i]-=k;
                k<<=1;
            }
            zero(num[i]*w[i]);
        }
    }
}
int main()
{
    while(scanf("%d",&v)!=EOF)
    {
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            scanf("%d%d",&num[i],&w[i]);
        }
        memset (dp,0,sizeof(dp));
        mul();
        int ans=0;
        for (int i=1;i<=v;i++)
        {
            ans=max(ans,dp[i]);
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值