完全背包、多重背包练习总结

完全背包练习总结

这两天又整了哈完全背包问题,跟01背包确实有很多相似的地方,但还是要花点时间研究熟练才得行,要不然比赛的时候遇到浪费时间精力

第一题 poj1384
这道题恩是不走寻常路,一般背包嘛都是求最大的价值,他非要求最小的价值。题目就是有一个长得像猪的层钱罐儿(不是耙耳朵好),给出他的自重和加上里面的钱的总重量,然后再给出几种票子的价值和重量,问最惨的情况里面存了好多钱。其实我小时候也有个层钱罐儿,我小时候我爸给我了好多硬币哦,我都存了几大百了~,这道题我觉得应该算是完全背包的模板题(这个“模”打了半天,然后突然想起小学班主任教的念“mu”,不是“mo” 一模(mu)一样)。 完全背包和01背包的区别就是完全背包的数量不止一个,有可能多得很,也有可能就那么几个。

01背包是这个套路
这里写图片描述

这是完全背包
这里写图片描述

然后代码

#include<iostream>
#include<cstring>
using namespace std;
int length;
int shuzu[10005];
int w[501], v[501];

int main(){
    int jishu;
    cin >> jishu;
    while(jishu -- ){       
        memset(shuzu, 0x3f, sizeof(shuzu));
        shuzu[0] = 0;
        int a , b;
        cin >> a >> b;
        length = b - a + 1;
        int num ;
        cin >> num;
        for(int i = 0; i < num; i++) cin >> v[i] >> w[i];

        for(int i = 0; i < num; i++){
            for(int j = 0; j+w[i] < length; j++){
                if(j%w[i]!=0 && shuzu[j]==0x3f3f3f3f) continue;
                if(shuzu[j+w[i]] > shuzu[j]+v[i])
                   shuzu[j+w[i]] = shuzu[j] + v[i];
            } 
        }

        if(shuzu[length-1] == 0x3f3f3f3f) 
            cout << "This is impossible." << endl;      
        else 
            cout << "The minimum amount of money in the 
            piggy-bank is "<<shuzu[length-1]<<"." << endl;
    } 
    return 0;
}

多重背包

第二题 poj1787
这道题是有1块钱的硬币、5块钱的硬币、10块钱的硬币、25块钱的硬币(居然还有25块钱的,不晓得有没得二百五一张的),然后就是就是给一个价值总数,问用硬币数最大的情况下,分别这四种硬币用的数量。这道题我还是用背包的动态规划套路,一种硬币一种硬币的拿进来考虑,然后我就卡了,紧到WA,关键时我始终觉得我绝对是对的。经过研究,我发现我异想天开的觉得从1——5——10——25 这样的顺序找下来,因为是从小的开始找,所以只要第一个找到可以凑出来的情况就是,因为前面的价值小用的自然就最多三,后头的看都不用看了,但是我确实错了

1块钱:25张 , 5块钱:12张 , 10块钱:0张 , 25块钱:1张
要求凑够60块钱
然后就一种钱一种钱的加进来看:
1块钱: 凑不够
5块钱: 可以!! 12张5块刚刚60三 (5角钱~~~)
10块钱:得都莫得说啥子喃说
25块钱:可以一张25块钱+25张1块钱,这儿就有26张了,所以不能一发现凑够就结束

所以我就把这四种钱对应的数量整成一个结构体,然后在完全背包那个套路的时候的时候就对应的加上对应种类钱的数量

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef struct Node{
    int data;
    int arr[4];
    Node(){
        data = 0;
        arr[0] = arr[1] = arr[2] = arr[3] = 0;
    }
}Node; 
int length;
int coin[4];
Node shuzu[10010];
int value[4] = {1, 5, 10, 25};

void tovalue(Node &n1, Node &n2){
    n1.data = n2.data;
    for(int i = 0; i < 4; i++) 
        n1.arr[i] = n2.arr[i];
}

void print(){
        for(int i = 0; i <= length; i++){
            cout << " (";
            for(int j = 0; j < 4; j++){
                cout << shuzu[i].arr[j] << " ";
//              cout << shuzu[i].data << " ";
            }
            cout << shuzu[i].data << " ";
            cout <<"\b)" ;
        }
        cout << endl;
}

int main(){
    while(cin >> length >> coin[0] >> coin[1] >> coin[2] >> coin[3]){
        if(!length && !coin[0] && !coin[1] 
            && !coin[2] && !coin[3]) break;
        memset(shuzu, 0, sizeof(shuzu));
        for(int i = 0; i < 4; i++){
            for(int j = 1; j <= length; j++){
                if(shuzu[j].data == 0){
                   if(j%value[i]==0 && j/value[i]<=coin[i]){
                      shuzu[j].data = j/value[i];
                      shuzu[j].arr[i] = j/value[i];
                   }
                }else{
                   if(shuzu[j+value[i]].data >= shuzu[j].data+1 
                      || shuzu[j].arr[i]>=coin[i] 
                      || j+value[i]>length) continue;
                   tovalue(shuzu[j+value[i]], shuzu[j]);
                   shuzu[j+value[i]].data ++;
                   shuzu[j+value[i]].arr[i] ++;
                }
            }
//          print();
        }

        if(shuzu[length].data!=0) 
           printf("Throw in %d cents
           , %d nickels, %d dimes, and %d quarters."
           ,shuzu[length].arr[0],shuzu[length].arr[1]
           ,shuzu[length].arr[2],shuzu[length].arr[3]);
        else 
           cout << "Charlie cannot buy coffee.";
        cout << endl;
    }
    return 0;
}

第三道题 poj2062
这道题简直把我整腾儿了,其实就怪字母“K” 和 “k”,长得有点像,哎,我还一直在研究到底是我的算法哪错了,但是我始终都没找到哪有错,我有些时候都怀疑是不是poj数据有错哦,我都有点想找他们投诉了~~~
题就是说耍扑克,把双王去了,还剩52张牌,一共有13种点数“A23456789TJQK”,然后就是有两个人来耍,每个人发26张以内的牌,然后分别排列,在对应的位置上哪个大那个就的一分。反正不管啥子牌,都是要转化成一个对应的数值才好比较大小
比如A这个人有 33、43、53三张牌
然后B这个人有 31、41、51三张牌
咋个才能让B赢而且赢得点数最多,是不是感觉好像在哪看到过,对啊,就是小学学的《田忌赛马》,就用31——53,先用最菜的把最强的干掉,然后剩到的几个就问撵了三
用41——33,51——43.
然后我还是想了一些错的思路,就是紧到AC不到,然后我也不是很清楚为啥错,只是大概感觉了哈,然后就换思路嘛,正确的就是把他们所有牌排序(虽然我这个方法其实不怎么样,应该是我很多方法都不怎么样,因为网上一搜好多高手的思路、代码有简单又清晰,但毕竟还是我想多久整出来了嘛,还是记录一哈)
把所有排序:
31(B) , 33(A) , 41(B) , 43 (A) , 51(B) , 53(A)
然后一个一个对应切消,这时候就要用到STL里面的list链表了,从左到右先找一个A的牌,然后从这张牌开始往后找一张B的牌,找到的话就把他们两个都消了,找不到就退出循环。。。其实感觉都已经不是背包问题了,是二分图最大匹配问题。
然后我还是WA了很多次,就是因为那个“k”(“K”)

#include<iostream>
#include<list>
using namespace std;
typedef struct Node{
    int which;
    int data;
    Node(int w, int d){
        which = w; data = d;
    }
}Node;
int length;
int value[100] = {0};

bool cmp(Node a, Node b){
    return a.data < b.data;
}

void print(list<Node> &link){
    list<Node>::iterator start = link.begin();
    list<Node>::iterator end = link.end();
    for(; start != end; start ++) 
    cout << "(" <<  (*start).data << "," 
    << (*start).which<< ")  "<< " " ; cout << endl;
}

int main(){ 
    for(int i = 2; i < 10; i++) value[i+'0'] = i * 10;
    value['T'] = 100; value['J'] = 110; value['Q'] = 120; 
    value['K'] = 130;value['H'] = 4;   value['C'] = 1;   
    value['D'] = 2;   value['S'] = 3;
    value['A'] = 200;
    int jishu;
    cin >> jishu;
    while(jishu --){
        list<Node> link;
        cin >> length;
        char a, b;
        for(int i = 0; i < 2; i++){
            for(int j = 0; j < length; j++){
                cin >> a >> b;
                link.push_back(Node(i+1, value[a] + value[b]));
            }
        }

        int answer = 0;
        link.sort(cmp);
//      print(link);
        list<Node>::iterator start = link.begin(), qian = start;
        list<Node>::iterator end = link.end();      
        while(start != end){            
            while((*start).which!=1 && start!=end) start++;
            if(start == end) break;
            qian = start;
            while(((*qian).which!=2 || (*qian).data==(*start).data) 
            && qian!=end) qian ++;
            if(qian == end) break;
            answer++;
            link.erase(qian);
            start = link.erase(start);          
            end = link.end();
        }

        cout << answer << endl;
    }
    return 0;
}

/*
1
6
JD 5D TC 2H 3H 4H
JH 4C 5H 2D 3D 4D
*/

第四五题 poj3181 poj2229
这两道题很类似,就是求一个数比如说5
5 = 1 + 1 + 1 + 1 + 1
5 = 2 + 1 + 1 + 1
5 = 2 + 2 + 1
5 = 3 + 1 + 1
5 = 3 + 2
懂三~ 给一个数,然后给点限制,求有好多种不同的组合
dp就是先只有1的时候,更新数组,只有2的时候,跟新数组。。。。
1: 1 , 1 , 1 , 1 , 1
2: 1 , 2 , 2,3,3
3: 1,2,3,4,5
4: 1,2,3,4,6
dp[i][j] 就代表j这个数,在分解元素的时候,取i为最大元素时,有多少种情况
比如 j = 10, i = 3的时候,就是取10所有组合中3为当前组合中最大的情况,所以就先把3从10里面取出来,然后就只剩7,而7又在10的前面已经dp出来了(其实7也是如法炮制,从7里面取出3,还剩4,然后又且看4。。。,动态规划已经把后面需要的数据都保存下来了)
poj3181需要处理一哈数据,因为太大了long long装不下,double又不精准。。。

#include<iostream>    // 17
#include<ctime>
using namespace std;
#define N 100000000000000000
typedef struct Node{
    long long rear,front;
    Node(){rear = front = 0;}
}Node;
int length,row;
Node shuzu[105][1005];


int main(){ 
    cin >> length >> row;
    int clock1 = clock();
    for(int i = 1; i <= length; i++) {
          shuzu[1][i].rear = 1; 
          shuzu[1][i].front = 0;
    }
    for(int i = 2; i <= row; i++){
        for(int j = 1; j < i; j++){
            shuzu[i][j].rear = shuzu[i-1][j].rear;
            shuzu[i][j].front = shuzu[i-1][j].front;
        }
        shuzu[i][i].front = shuzu[i-1][i].front 
                            + (shuzu[i-1][i].rear+1)/N;
        shuzu[i][i].rear = (shuzu[i-1][i].rear + 1) % N;
        for(int j = i+1; j <= length; j++){
            shuzu[i][j].front = shuzu[i][j-i].front 
                                + shuzu[i-1][j].front 
                                + (shuzu[i][j-i].rear
                                   +shuzu[i-1][j].rear)/N;
            shuzu[i][j].rear = 
                     (shuzu[i][j-i].rear + shuzu[i-1][j].rear) % N;
        }
    }

    if(shuzu[row][length].front != 0)
        cout << shuzu[row][length].front;
    cout << shuzu[row][length].rear << endl;
//  cout << clock() - clock1 << endl;
//6604049133121382808    156581811045807 71094597751280645
    return 0;
}

然后就是poj1742 poj1882 poj2063 poj1276 poj2392
poj1742 是代表,经典的多重背包
这里写图片描述
这里写图片描述

#include<iostream>
#include<cstring>
using namespace std;
int row, length;
int v[105], count[105];
int shuzu[100005];

int main(){
    while(cin >> row >> length && row && length){
        for(int i = 0; i < row; i++) cin >> v[i];
        for(int i = 0; i < row; i++) cin >> count[i];
        memset(shuzu, -1, sizeof(shuzu));
        shuzu[0] = 0;
        for(int i = 0; i < row; i++){
            for(int j = 0; j <= length; j++){
                if(shuzu[j] >= 0){
                    shuzu[j] = count[i];
                }else if(j-v[i]<0 || shuzu[j-v[i]]<=0){
                    shuzu[j] = -1;
                }else{
                    shuzu[j] = shuzu[j-v[i]] - 1;
                }
            }
        }

        int answer = 0;
        for(int i = 1; i <= length; i++) if(shuzu[i]>=0) answer++;
        cout << answer << endl;
    }
    return 0;
}
展开阅读全文

没有更多推荐了,返回首页