lintcode---杆子分割

题目描述:
给一个 n 英寸长的杆子和一个包含所有小于 n 的尺寸的价格. 确定通过切割杆并销售碎片可获得的最大值.例如,如果棒的长度为8,并且不同长度部件的值如下,则最大可获得值为 22(通过切割两段长度 2 和 6 )。

样例:

长度    | 1   2   3   4  5   6   7   8  
价格    | 1   5   8   9  10  17  17  20

给出 price = [1, 5, 8, 9, 10, 17, 17, 20], n = 8 返回 22//切成长度为 2 和 6 的两段

长度    | 1   2   3   4  5   6   7   8  
价格    | 3   5   8   9  10  17  17  20

给出 price = [3, 5, 8, 9, 10, 17, 17, 20], n = 8 返回 24//切成长度为 1 的 8 段

标签:
背包问题、动态规划

思路讲解:
我第一眼看到的想法就是利用动态规划中的01背包问题来做,不过,这里不仅仅是简单的01背包问题,因为这里允许每一种可以拿多个,这就导致其跟原来的不一样了(原来的是拿与不拿的问题),这样的问题我们成为完全背包问题,我选择了其中的两种做法进行操作:
第一种是将这种问题转换为简单的01背包问题,我们可以将每一种长度物品变为n个一样长度的物品(这里的n是背包能装的最多个数)这样就转换为01背包问题了。
举个例子:
原本的数据:
棒的长度为4

长度    | 1   2   3 
价格    | 3   5   8 

转换后为:

长度    | 1   1   1   1   2   2   3  
价格    | 3   3   3   3   5   5   8

这样我们就可以继续使用01背包问题的状态转移方程map[i][j]=max(map[i-1][j],(map[i][j-weight[i-1]]+prices[i-1]))对这个问题进行求解了。

详细代码:

class Solution {
public:
    /*
     * @param : the prices
     * @param : the length of rod
     * @return: the max value
     */
    int cutting(vector<int>& prices, int n) {
        // Write your code here
        int N=prices.size();
        int V=n;

        vector<int>weight;
        for(int i=0;i<N;i++)
        {
            weight.push_back(i+1);
        }
        for(int i=0;i<N;i++)
        {
            int count=n/weight[i];

            int tmp1=weight[i];
            int tmp2=prices[i];
            for(int j=1;j<count;j++)//将无数个转换为具体的个数
            {
                weight.push_back(tmp1);
                prices.push_back(tmp2);
            }
        }
        N=weight.size();
        V=n;
        sort(weight.begin(),weight.end());
        sort(prices.begin(),prices.end());

        int **map=new int*[N+1];
        for(int i=0;i<=N;i++)
        {
            map[i]=new int[V+1];
        }
        for(int i=0;i<=N;i++)
        {
            for(int j=0;j<=V;j++)
            {
                map[i][j]=0;
            }
        }

        for(int i=1;i<=N;i++)
        {
            for(int j=1;j<=V;j++)
            {
                map[i][j]=max(map[i-1][j],j-weight[i-1]>=0?(map[i][j-weight[i-1]]+prices[i-1]):0);//状态转移方程
            }
        }
        //printvector(weight);
       // printvector(prices);
        //print2Darray(map,N,V);
        return map[N][V];
    }


    void print2Darray(int **p,int a,int b)
    {
        for(int i=0;i<=a;i++)
        {
            for(int j=0;j<=b;j++)
            {
                cout<<p[i][j]<<" ";
            }
            cout<<endl;
        }
    }
    void printvector(vector<int>res)
    {
        for(int i=0;i<res.size();i++)
        {
            cout<<res[i]<<" ";
        }
        cout<<endl;

    }
};

第二种方法:由于在原来的01背包问题中,我们只需要选择选或者不选,但是这里我们不仅要决定选或者不选,还要决定选取的个数即取0个、取1个、取2个、取3个直到背包放不下。所以,我们可以对原来的状态转移方程进行改造原来的是map[i][j]=max(map[i-1][j],(map[i][j-weight[i-1]]+prices[i-1])),我们可以发现这里-weight[i]只减去一个,所以我们可以在这里加上一个变量K,即变成map[i][j]=max(map[i][j],map[i-1][j-k*i]+k*prices[i-1])这样,我们只需要在每一次循环K的个数即可,(0<=K<=n/weight[i])即可。

详细代码:

class Solution {
public:
    /*
     * @param : the prices
     * @param : the length of rod
     * @return: the max value
     */
    int cutting(vector<int>& prices, int n) {
        // Write your code here
        int N=prices.size();
        int V=n;
        int **map=new int*[N+1];
        for(int i=0;i<=N;i++)
        {
            map[i]=new int [V+1];
        }
        for(int i=0;i<=N;i++)
        {
            for(int j=0;j<=V;j++)
            {
                map[i][j]=0;
            }
        }
        for(int i=1;i<=N;i++)
        {
            for(int j=1;j<=V;j++)
            {
                int ncount=j/i;
                for(int k=0;k<=ncount;k++)
                {
                    map[i][j]=max(map[i][j],map[i-1][j-k*i]+k*prices[i-1]);//重点的转移方程
                }
            }
        }
        //print2Darray(map,N,V);
        return map[N][V];
    }


    void print2Darray(int **p,int a,int b)
    {
        for(int i=0;i<=a;i++)
        {
            for(int j=0;j<=b;j++)
            {
                cout<<p[i][j]<<" ";
            }
            cout<<endl;
        }
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值