01背包(hdu-题集)

总结之前:先感谢一下http://blog.csdn.net/yexiaohhjk/article/details/50229489

博主的所提供的题目及题解。

其状态转移方程是:

f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

1. HDU2602:Bone Collector
这题就是裸题:有两种形式写法:

题目给出T(示例个数):N(骨头个数)V(背包大小)

val{1,2,3,4,5}//对应编号骨头的价值。

w{5,4,3,2,1,}//对应编号骨头的容量。

表格第一行和第一列是故意空出来的。

以编号为行标,以容量为纵标。所以才有    6*11的表格,

根据    f[i][v]    =    max    {    f[i-1][v]    ,    f    [i-1][v-c[i]]    +w[i]    };

制作表格。答案就是最后的dp[N][V];

0      0      0      0      0      0      0      0      0      0      0  
0      0      0      0      0      1      1      1      1      1      1  
0      0      0      0      2      2      2      2      2      3      3  
0      0      0      3      3      3      3      5      5      5      5  
0      0      4      4      4      7      7      7      7      9      9  

0      5      5      9      9      9      12   12    12    12  14

代码实现:

#include
#include
#include
typedef long long ll;
using namespace std;
int main()
{
    int T;
    cin>>T;
    while(T--){
        int N,V;
        cin>>N>>V;
        int w[1001],val[1001];
        int dp[1001][1001];
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=N;i++){
            cin>>val[i];
        }for(int i=1;i<=N;i++){
            cin>>w[i];
        }for(int i=1;i<=N;i++){
            for(int j=0;j<=V;j++){
                if(j>=w[i]){
                    dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+val[i]);
                }else{
                    dp[i][j]=dp[i-1][j];
                }
            }
        }
        /*for(int i=0;i<=N;i++){
            for(int j=0;j<=V;j++){
                printf("%-3d",dp[i][j]);
            }printf("\n");
        }*/
        printf("%d\n",dp[N][V]);
    }return 0;
}

其实还有一些更省空间和时间的方法。

一维数组处理数据

for(int i=0;ii++)

for(int j=V;j>=weight[i];j--)

dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);

因为大家可以想象一下,其实二维也是根据上一列的    dp[i-1][j-weight[i]] 处理的

浪费空间,所以可以压缩成一维处理,处理时也是和二维的一样,不过是直接根据dp[j-weight[i]]比较

注意一点的是,里面的for循环,是从V开始的,目的是保证 在能放的前提下,放东西,而且为什么从后往前

而不是从for j=weight[i] ~V;

这是因为物品只能放一遍,决策时有一个念头就是快满了(比如剩下一个物品),就能放就放,不必考虑最优的情况

参考代码:

#include
#include
#include
#include
using namespace std;
int main()
{
    int T;
    cin>>T;
    while(T--){
        int N,V,w[1001],dp[1001],val[1001];
        memset(dp,0,sizeof(dp));
        cin>>N>>V;
        for(int i=1;i<=N;i++){
            cin>>val[i];
        }for(int i=1;i<=N;i++){
            cin>>w[i];
        }for(int i=1;i<=N;i++){
            for(int j=V;j>=w[i];j--){
                dp[j]=max(dp[j],dp[j-w[i]]+val[i]);
            }
        }printf("%d\n",dp[V]);
    }
    return 0;
}

HDU-2546-饭卡(01背包+一点细节)

其实这题也是裸的01背包题。但是加了一点细节,就是加了一个5块的特权,

贪心的想法出发,要是我能用最后的5块买任何东西为什么不买最贵的东西呢,

一般的花费是  m,m-a[i]........8、7、6、5、4、3、2、1、0.

解题方法 是  m、m-1、、、、、、、6     五块单独拿出来

用m-5的容量装东西,剩下5块买最贵的物品

详细代码:

#include
#include
#include
#include
using namespace std;
int main()
{
    int n;
    while(cin>>n&&n){
        int a[1050],m;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        cin>>m;
        if(m<5){
            printf("%d\n",m);
            continue;
        }
        int dp[1050];
        m-=5;
        sort(a+1,a+n+1);
        memset(dp,0,sizeof(dp));
        for(int i=1;i=a[i];j--){
                dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
            }
        }
            cout<

Big Event in HDU(HDU-1171)

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

这题的大意是,尽量分成两份,这题是01背包的变形

以sum/2为容量,让它尽量放满。

a-代表的是价值,b-是数量。我们处理这问题还需要考虑离散化。

#include
#include
#include
#include
using namespace std;
int main()
{
    int n;
    while(cin>>n){
        if(n<=0)return 0;
        int dp[300000]={0};
        int cnt=0,p[5050],sum=0;
        while(n--){
            int a,b;
            cin>>a>>b;
            while(b--){
                p[cnt++]=a;
                sum+=a;
            }
        }
        for(int i=0;i=p[i];j--){
                dp[j]=max(dp[j],dp[j-p[i]]+p[i]);
            }
        }
        printf("%d %d\n",max(sum-dp[sum/2],dp[sum/2]),min(sum-dp[sum/2],dp[sum/2]));
    }
    return 0;
}

Bone Collector II

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

这题是找第K大的背包,大家思考一下,

就如要找年级前十名,我需要的是各个班里前十名的成绩,

目前就是两个班,我想要的是第K名,我需要的是两个K 。

有两个代码 ,第一是discuss里面的,第二个是别人的博客上的。

#include
using namespace std;
const int maxn=1006;
int dp[maxn][maxn];
int w[maxn],val[maxn],v[maxn],cnt;
int n,W,K;
bool cmp(int x,int y)
{
    return x>y;
}
void solve()
{
    for(int i=0;i=w[i];j--){
            cnt=0;
            for(int th=1;th<=K;th++){
                val[cnt++]=dp[j][th];
                val[cnt++]=dp[j-w[i]][th]+v[i];
            }sort(val,val+cnt,cmp);
            cnt=unique(val,val+cnt)-val;
            for(int th=1;th<=min(K,cnt);th++){
                dp[j][th]=val[th-1];
            }
        }
    }
}
int main()
{
        int T;
        cin>>T;
        while(T--){
            cin>>n>>W>>K;
            memset(dp,0,sizeof(dp));
            for(int i=0;i>v[i];
            for(int i=0;i>w[i];
            solve();
            cout<
#include
using namespace std;
int dp[1010][50],A[50],B[50],value[110],cost[110];
int main()
{
    int T;
    cin>>T;
    while(T--){
        int n,k,v;
        cin>>n>>v>>k;
        memset(dp,0,sizeof(dp));
        for(int i=0;i>value[i];
        for(int i=0;i>cost[i];
        for(int i=0;i=cost[i];j--){
                for(int kk=0;kkB[b])dp[j][c]=A[a],a++;
                    else dp[j][c]=B[b],b++;
                    if(dp[j][c]!=dp[j][c-1]) c++;
                }
            }
        }
        cout<

Robberies

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

输入的变量有点多,

T

P、N

m1 p1

....

m(n) ,p(n).

P指的是总的被抓概率,而最后的答案一定是比它要小的。

Pi-是偷每一个的银行,被抓的概率。

这题是一个思维的转变。首先是打破我们以往的背包容量的概念,

要是不告诉你概率的精度可能是0.00000001的话,你可能就把概率当成容量进行01背包。

无可奈何就是需要,把钱当作容量,要是dp[m]=1-p    ;    m是钱,而1-p是逃跑概率。

大家可以设想一下,要是dp[0]=1,逃跑概率达到100%;  

只有逃跑率才会等于各个逃跑率之积,被抓的概率不会等于各个被抓的概率之积

最后dp[i]是拿i钱的逃跑率,dp[ans]>P。for(int i=sum~0)一旦找到逃跑率大的就输出

虽然说比较复杂但是,多想还是很有意思的。

#include
using namespace std;
int main()
{
    int T;
    cin>>T;
    while(T--){
        double p[110],P,dp[10010];
        memset(dp,0,sizeof(dp));
        int sum=0,v[110],n;
        cin>>P>>n;
        P=1.0-P;
        for(int i=0;i>v[i]>>p[i];
            p[i]=1.0-p[i];
            sum+=v[i];
        }
        dp[0]=1.0;
        for(int i=0;i=0;j--){
                dp[j]=max(dp[j],dp[j-v[i]]*p[i]);
            }
        }
        for(int i=sum;i>=0;i--){
            if(dp[i]-P>0.000000001){
                cout<

HDU_3466    Proud Merchants

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

N、M

有N行,M是自己拥有的金币。

然后有N行数据: Pi, Qi and Vi  

        这道题和01背包很类似,唯一不同的就是买每件物品时多了一个限制条件,p,Q,v,需要钱数大于Q才能买,
所以买东西需要有先后顺序。以前的01背包不用排序,dp[j] = max(dp[j],dp[j-w[i]]+v[i])表达的是这第i个物品要不要买,
但同时也在判断第i个物品要不要先买,如果先买剩下的空间随便放,看是否得到的价值会更大。
        A物品  p1,q1,v1;
        B物品  p2,q2,v2
        若先买A,至少需要p1+q2 (p1 == q1情况下)若现买B,至少需要p2+q1;
        若A q2-p2;即差值大的先买,所以先买的应排到后面


加了排序的01背包。

#include
using namespace std;
typedef struct point{
    int p,Q,v,m;
}point;
int cmp(point a,point b)
{
    return a.Q-a.p=a[i].Q;j--){
                dp[j]=max(dp[j],dp[j-a[i].p]+a[i].v);
            }
        }
        cout<

HUD-1864发票问题

该题要注意的就是每张单子A种类的总和不能大与600,同样B,C类也一样,

还有注意如果不是A,B,C类的不可以报销;

该题就是要把浮点型变成整数这样才能用01背包,这里就只要乘以100就可以了。

Problem Description
现有一笔经费可以报销一定额度的发票。允许报销的发票类型包括买图书(A类)、文具(B类)、差旅(C类),
要求每张发票的总额不得超过1000元,每张发票上,单项物品的价值不得超过600元。
现请你编写程序,在给出的一堆发票中找出可以报销的、不超过给定额度的最大报销额。
 
Input
测试输入包含若干测试用例。每个测试用例的第1行包含两个正数 Q 和 N,其中 Q 是给定的报销额度,N(<=30)是发票张数。随后是 N 行输入,每行的格式为:
m Type_1:price_1 Type_2:price_2 ... Type_m:price_m
其中正整数 m 是这张发票上所开物品的件数,Type_i 和 price_i 是第 i 项物品的种类和价值。物品种类用一个大写英文字母表示。当N为0时,全部输入结束,相应的结果不要输出。
 
Output
对每个测试用例输出1行,即可以报销的最大数额,精确到小数点后2位。
 
Sample Input
200.00 3
2 A:23.50 B:100.00
1 C:650.00
3 A:59.99 A:120.00 X:10.00
1200.00 2
2 B:600.00 A:400.00
1 C:200.50
1200.50 3
2 B:600.00 A:400.00
1 C:200.50
1 A:100.00
100.00 0
 
Sample Output
123.50
1000.00
1200.50
 

#include
using namespace std;
int dp[3000010];
int p[50];
int main()
{
    double q;
    int n;
    while(~scanf("%lf%d",&q,&n),n)
    {
        int Q=(int)(q*100),cnt=0;
        while(n--)
        {
            int t,a=0,b=0,c=0,flag=0;
            scanf("%d",&t);
            while(t--)
            {
                char ch;   double f;
                scanf(" %c:%lf",&ch,&f);
                switch(ch){
                    case 'A':a+=f*100;break;
                    case 'B':b+=f*100;break;
                    case 'C':c+=f*100;break;
                    default:flag=1;break;
                }
                if(a+b+c>100000||a>60000||b>60000||c>60000) flag=1;
            }
            if(flag) continue;
            else p[cnt++]=a+b+c;
        }
        memset(dp,0,sizeof(dp));
        for(int i=0;i=p[i];j--){
                dp[j]=max(dp[j],dp[j-p[i]]+p[i]);
            }
        }
        printf("%.2lf\n",dp[Q]/(100.0));
    }
}

dream city

题意:有n颗树,每棵树都有固定的初始值a[i],和有增长值b[i],给出m天,每天只能砍一棵树,那么对与第j天,

如果选择砍掉第i棵树,那么收获的硬币就是a[i]+b[i]*(j-1),那么怎样砍才能使收获的硬币最多

思路:此时,我们给出的贪心策略是:增长越快的树越晚砍,为什么?

有人可能觉得如果一棵树的a[i]很大,尽管b[i]很小,放在后面也能得到较高的值,其实这样想是错的。

可以想下,要是我们已经得出了一种最优方案,砍掉了e颗树,那么我们所收获的硬币是不是就是 

sum=a[1]+a[2]+b[2]+a[3]+(b[3]*2)+.......+a[e]+(b[e]*(e-10));进行下化简

sum=a[1]+a[2]+...+a[e]+b[2]+(b[3]*2)+....+(b[e]*(e-1)),可以看到,前面那一串a数组加起来的值是固定的,

sum最后的值其实只和b什么时候砍有关,所以必须保证b越大越晚砍

每棵树可以选择砍与不砍,所以这是一个01背包问题,排完序后直接上01背包模板就行,

下面给出记忆化搜搜和递推的写法

#include
#include
#include
#define mp make_pair

using namespace std;

typedef pair pii;

pii a[300];
int n,m,t,u,v;
int dp[300][300];

bool cmp(const pii a,const pii b)
{
    return a.second
#include 
#include 
#include 

using namespace std;

typedef pair pii;

pii a[300];
int dp[300][300];
int n,m;


bool cmp(const pii a,const pii b)
{
    return a.secondm||cur>n) return 0;
    if(dp[cur][day]!=-1) return dp[cur][day];
    int &res=dp[cur][day];
    res=max(dfs(cur+1,day),dfs(cur+1,day+1)+a[cur].first+a[cur].second*(day-1));
    return res;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i].first);
        }
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i].second);
        }
        sort(a+1,a+n+1,cmp);
        memset(dp,-1,sizeof(dp));
        int ans=dfs(1,1);
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(01背包(hdu-题集))