动态规划

1.01背包

hdu 2602 Bone Collector(01背包)

题意:给出包裹的大小v,然后给出n块骨头的价值value和体积volume,求出一路下来包裹可以携带骨头最大价值

思路:01背包

1.二维数组(不常用

/*
01背包
二维数组
*/
#include<iostream>
#include<stdio.h>
#include<math.h>
using namespace std;
int dp[1100][1100];
int main()
{
    int i,j;
    int t,n,_v;//测试用例个数,物品种类,背包大小
    int c[1100],v[1100];//花费,价值
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&_v);
        for(j=0; j<=_v; ++j)dp[0][j]=0;

        for(i=1; i<=n; ++i)scanf("%d",&v[i]);
        for(i=1; i<=n; ++i)scanf("%d",&c[i]);

        for(i=1; i<=n; ++i)
        {
            for(j=0; j<c[i]; ++j)dp[i][j]=dp[i-1][j];
            for(j=c[i]; j<=_v; ++j)
            {
                if(dp[i-1][j-c[i]]+v[i]>dp[i-1][j])dp[i][j]=dp[i-1][j-c[i]]+v[i];
                else dp[i][j]=dp[i-1][j];
            }
        }

        printf("%d\n",dp[n][_v]);
    }
    return 0;
}
View Code

2.一维数组(之后 多重、完全背包 的解法 皆采用一维数组)

1...n
   _v...0

/*
01背包
一维数组
*/
#include<iostream>
#include<stdio.h>
using namespace std;
int dp[1100];
int main()
{
    int i,j;
    int t,n,_v;//测试用例个数,物品种类,背包大小
    int c[1100],v[1100];//花费,价值
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&_v);
        for(j=0; j<=_v; ++j)dp[j]=0;

        for(i=1; i<=n; ++i)scanf("%d",&v[i]);
        for(i=1; i<=n; ++i)scanf("%d",&c[i]);

        for(i=1; i<=n; ++i)
            for(j=_v; j>=c[i]; --j)
                if(dp[j-c[i]]+v[i]>dp[j])dp[j]=dp[j-c[i]]+v[i];

        printf("%d\n",dp[_v]);
    }
    return 0;
}
View Code

 

2.多重背包

hdu 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(多重背包)

题意:给出经费的最大值n,再给出   种类m和每种的袋数c、价格p、重量h,求能买大米的最大重量

思路:每种物品有一个固定的次数上限。为多重背包问题。转换为01背包来做

以下方法,均为转化为01背包来做

思路1:物品不摊开,选取每一种时,进行讨论,(相当于竖着填背包v的一列,一列一列的填

码1:kj

/*
多重背包
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int dp[110];
int main()
{
    int i,j,k,tem;
    int t,n,_v;//测试用例个数,物品种类,背包大小
    int c[110],v[110];//花费,价值
    int num[110];//每种物品个数
    int tc,tv;//拆分时物品花费,价值
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&_v,&n);
        memset(dp,0,sizeof(dp));

        for(i=1; i<=n; ++i)scanf("%d%d%d",&c[i],&v[i],&num[i]);
        //////
        for(i=1; i<=n; ++i)
            for(k=_v; k>=c[i]; --k)
                for(j=1; j<=num[i]&&j*c[i]<=k; ++j)//此处比01背包多了一层循环
                {
                    tc=j*c[i];
                    tv=j*v[i];
                    tem=dp[k-tc]+tv;
                    if(tem>dp[k])dp[k]=tem;
                }
        //
        printf("%d\n",dp[_v]);
    }
    return 0;
}
View Code

思路2:物品摊开,然后处理01背包(相当于横着填物品的一行,一行一行的填

2.1朴素拆分,把每种物品展开,既有n件a物品,则进行n件是否选取操作

码2:jk

/*
多重背包
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int dp[110];
int main()
{
    int i,j,k,tem;
    int t,n,_v;//测试用例个数,物品种类,背包大小
    int c[110],v[110];//花费,价值
    int num[110];//每种物品个数
    int tc,tv;//拆分时物品花费,价值
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&_v,&n);
        memset(dp,0,sizeof(dp));

        for(i=1; i<=n; ++i)scanf("%d%d%d",&c[i],&v[i],&num[i]);
        //////
        for(i=1; i<=n; ++i)
            for(j=1; j<=num[i]&&j*c[i]<=_v; ++j)//此处比01背包多了一层循环
                for(k=_v; k>=j*c[i]; --k)
                {
                    tem=dp[k-c[i]]+v[i];
                    if(tem>dp[k])dp[k]=tem;
                }
        //
        printf("%d\n",dp[_v]);
    }
    return 0;
}
View Code

hint:2.1代码与思路1 代码 只是循环次序不一样而已,实际是一样的操作。(并且时间复杂度相同,略麻烦。
因为 
思路1:
for(k=_v; k>=c[i]; --k)
      for(j=1; j<=num[i]&&j*c[i]<=k; ++j)
思路2:
for(j=1; j<=num[i]&&j*c[i]<=_v; ++j)
      for(k=_v; k>=j*c[i]; --k)

大体看一下这俩循环,

这两个循环做的循环次数是相同的,也就是说,计算次数相同,时间复杂度相同

思路2.2  二进制拆分,有n件a物品,则拆成 1,2,4,8,...,q 这样,即 1+2+4+...+q=n

码3:

/*
多重背包
二进制拆分
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int dp[110];
int main()
{
    int i,j,k,tem;
    int t,n,_v;//测试用例个数,物品种类,背包大小
    int c[110],v[110];//花费,价值
    int num[110];//每种物品个数
    int tc,tv;//拆分时物品花费,价值
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&_v,&n);
        memset(dp,0,sizeof(dp));

        for(i=1; i<=n; ++i)scanf("%d%d%d",&c[i],&v[i],&num[i]);
        //////
        for(i=1; i<=n; ++i)
        {
            for(j=1; j<=num[i]; num[i]=num[i]-j,j=j*2)//此处比01背包多了一层循环
            {
                tc=j*c[i];//拆分后物品花费
                tv=j*v[i];//
                for(k=_v; k>=tc; --k)
                {
                    tem=dp[k-tc]+tv;
                    if(tem>dp[k])dp[k]=tem;
                }
            }
            if(num[i]>0) //如果还有物品,num[i] 即相当于 1+2+4+...+q  中的 q
            {
                tc=num[i]*c[i];
                tv=num[i]*v[i];
                for(k=_v; k>=tc; --k)
                {
                    tem=dp[k-tc]+tv;
                    if(tem>dp[k])dp[k]=tem;
                }
            }
        }
    //
        printf("%d\n",dp[_v]);
    }
    return 0;
}
View Code

 

3.完全背包

hdu 1114 Piggy-Bank(完全背包)

题意:给出一个存钱罐的容量,给出n种硬币的价值p和重量w(注意:每种硬币可无限取)

1.如果存钱罐能够正好塞满,输出塞满存钱罐需要的最少硬币的价值。

2.若不能完全塞满,则输出impossible。

思路:每种物品可以放无限多次。所以为完全背包问题。此题是求最小值,为完全背包的变形。

注意初始化 dp[ 0 ]=0;

 

for i=1..N

    for v=0..V

        f[v]=max{f[v],f[v-cost]+weight}

 

/*
完全背包
*/
#include<iostream>
#include<stdio.h>
using namespace std;

int main(){
    int t,e,f;//测试用例,空存钱罐的重量,装满后存钱罐的重量
    int n,p,w;//硬币种类,价值,重量
    int dp[11000];//最小价值
    dp[0]=0;//初始化为0
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&e,&f);
        int v=f-e;//存钱罐的容量
        int i,j;
        for(i=1;i<=v;i++)
            dp[i]=1234567890;
        scanf("%d",&n);
        for(i=0;i<n;i++){
            scanf("%d%d",&p,&w);
            for(j=w;j<=v;j++){
                int tem=dp[j-w]+p;
                if(tem<dp[j])
                    dp[j]=tem;
            }
        }
        if(dp[v]==1234567890)
            printf("This is impossible.\n");
        else
            printf("The minimum amount of money in the piggy-bank is %d.\n",dp[v]);
    }
    return 0;
}
View Code

 

4.最长上升子序列O(nlogn)

/*
最长上升子序列O(nlogn)
*/
#include<iostream>
#include<stdio.h>
using namespace std;

const int MAXN=500010;
int a[MAXN],b[MAXN];
//b[k]是序列a中所有长度为k的递增子序列中的最小结尾元素值
//用二分查找的方法找到一个位置,使得num>b[i-1]并且num<b[i],并用num代替b[i]
int Search(int num,int low,int high){
    int mid;
    while(low<=high){
        mid=(low+high)/2;
        if(num>=b[mid])low=mid+1;
        else high=mid-1;
    }
    return low;
}
int DP(int n){
    int i,len,pos;
    b[1]=a[1];
    len=1;
    for(i=2;i<=n;i++){
        if(a[i]>=b[len]){//如果a[i]比b[]数组中最大还大直接插入到后面即可
            len=len+1;
            b[len]=a[i];
        }
        else{//用二分的方法在b[]数组中找出第一个比a[i]大的位置并且让a[i]替代这个位置
            pos=Search(a[i],1,len);
            b[pos]=a[i];
        }
    }
    return len;
}

int main(){

    a[1]=1;
    a[2]=2;
    a[3]=3;
    a[4]=0;

    printf("%d\n",DP(4));

    return 0;
}
View Code

 

posted @ 2016-04-08 09:17  gongpixin  阅读(260)  评论(0)    收藏  举报