ACM Steps_Chapter Three_Section3

来源:互联网 发布:网络走马灯是什么意思 编辑:程序博客网 时间:2024/05/19 14:17

Bone Collector

/*标准的01背包问题。状态转移方程f[i][v] = max{f[i-1][v-c[i]]+v[i],f[i-1][v]}*/#include<iostream>#include<string.h>#include<stdio.h>using namespace std;int main(){int T,N,V,f[1001],vol[1001],val[1001],tem;cin>>T;while(T--){cin>>N>>V;for(int i=0;i<N;i++){cin>>val[i];}for(int i=0;i<N;i++){cin>>vol[i];}memset(f,0,sizeof(f));for(int i=0;i<N;i++){for(int j=V;j>=vol[i];j--){tem=f[j-vol[i]]+val[i];if(f[j]<tem)f[j]=tem;}}cout<<f[V]<<endl;}system("pause");return 0;}

Coins

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;int dp[100010],w[110],c[110];int main(){    int n,m,i,j,k,cnt;    while( scanf("%d%d",&n,&m),n+m )    {        cnt=0;        memset(dp,0,sizeof(dp));        for(i=1;i<=n;i++) scanf("%d",&w[i]);        for(i=1;i<=n;i++) scanf("%d",&c[i]);        for(i=1;i<=n;i++)        {            if( c[i]*w[i] > m )  // 完全背包            {                for( j=w[i];j<=m;j++ )                    dp[j]= max( dp[j],dp[j-w[i]]+w[i] );            }             //多重背包 转化为 01背包 ,//把物品的个数 c[i] 拆分为2^0,2^1,……,2^k,c[i]-2^k+1; //1至c[i]中的数肯定能够分解为前面的数的和。就是十进制转二进制else            {                k=1;                               int num=c[i];                while( k < num )                {                    for( j=m; j>=k*w[i] ; j--)                        dp[j]=max( dp[j],dp[j-k*w[i]]+k*w[i]);  //拆分是对c[i]来说的,//ij具体操作时其实是把2^k个物品组合为一个物品来操作                    num-=k;                    k<<=1;                }                for( j=m;j>=num*w[i];j--)                    dp[j]=max( dp[j],dp[j-num*w[i]]+num*w[i] );            }        }        for(i=1;i<=m;i++) if( dp[i]==i ) cnt++;        printf("%d\n",cnt);    }    return 0;}

Ahui Writes Word

/*题目大意: Ahui写作文,每个单词都有复杂度和value,先输入N,C,代表整篇作文有n个单词, 然后作文的最大复杂度小于V。(1 ≤ N ≤ 100000, 1 ≤ C ≤ 10000),后面跟N行, 每行输入单词,还有单词的价值val跟单词的复杂度compl(0<=val<=com<=10),求在单词的复杂度不超过C的情况下,Ahui能够获得的最大价值。解题思路:一般的01背包会TLE,因为N * C = 1000000000;但是仔细考虑下,其实每个单词v跟c都小于等于10,所以这么大的输入量,必定有单词的价值跟复杂度是完全相同的,因为10*10=100;因此可以考虑用多重背包减小时间复杂度。P03: 多重背包问题题目:有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。基本算法:这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程:f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}复杂度是O(V*Σn[i])。转化为01背包问题另一种好想好写的基本方法是转化为01背包求解:把第i种物品换成n[i]件01背包中的物品,则得到了物品数为Σn[i]的01背包问题,直接求解,复杂度仍然是O(V*Σn[i])。但是我们期望将它转化为01背包问题之后能够像完全背包一样降低复杂度。仍然考虑二进制的思想,我们考虑把第i种物品换成若干件物品,使得原问题中第i种物品可取的每种策略——取0..n[i]件——均能等价于取若干件代换以后的物品。另外,取超过n[i]件的策略必不能出现。方法是:将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为1,2,4,...,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品。分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示,这个证明可以分0..2^k-1和2^k..n[i]两段来分别讨论得出,并不难,希望你自己思考尝试一下。这样就将第i种物品分成了O(log n[i])种物品,将原问题转化为了复杂度为<math>O(V*Σlog n[i])的01背包问题,是很大的改进。下面给出O(log amount)时间处理一件多重背包中物品的过程,其中amount表示物品的数量:procedure MultiplePack(cost,weight,amount)    if cost*amount>=V        CompletePack(cost,weight)        return    integer k=1    while k<amount        ZeroOnePack(k*cost,k*weight)        amount=amount-k        k=k*2    ZeroOnePack(amount*cost,amount*weight)希望你仔细体会这个伪代码,如果不太理解的话,不妨翻译成程序代码以后,单步执行几次,或者头脑加纸笔模拟一下,也许就会慢慢理解了。O(VN)的算法多重背包问题同样有O(VN)的算法。这个算法基于基本算法的状态转移方程,但应用单调队列的方法使每个状态的值可以以均摊O(1)的时间求解。由于用单调队列优化的DP已超出了NOIP的范围,故本文不再展开讲解。我最初了解到这个方法是在楼天成的“男人八题”幻灯片上。小结这里我们看到了将一个算法的复杂度由O(V*Σn[i])改进到O(V*Σlog n[i])的过程,还知道了存在应用超出NOIP范围的知识的O(VN)算法。希望你特别注意“拆分物品”的思想和方法,自己证明一下它的正确性,并将完整的程序代码写出来。*/#include<stdio.h> #include<string.h>  char ch[100] ; int nkind , total_comp ; int value[100024] , comp[100024] , fine[100024] , map[11][11]; int main () {     int pos , x , y ,sum , t ;     while ( scanf ( "%d%d" , &nkind , &total_comp ) != EOF )     {           memset( map , 0 , sizeof (map) ) ;           for ( int i = 0 ; i < nkind ; i ++ )           {               scanf ( "%s%d%d" , ch , &x , &y ) ;               map[x][y] ++ ;           }           pos = 0 ;                           // 用二分制转换成01背包            for ( int i = 0 ; i <= 10 ; i ++ )               for ( int j = 0 ; j <= 10 ; j ++ )                {                   if( map[i][j] >= 1  )                   {                        sum = 1 , t = 1 ;                       while ( sum <= map[i][j] )                       {                             value[pos] = i * t ;                             comp[pos++] = j * t ;                             t *= 2 ;                             sum += t ;                       }                       if ( ( sum - t ) < map[i][j] )                       {                            value[pos] = i * (map[i][j]-sum + t) ;                            comp[pos++] = j * (map[i][j]-sum + t) ;                       }                   }               }           memset( fine , 0 , sizeof (fine) ) ;           for ( int i = 0 ; i < pos ; i ++ )               for ( int j = total_comp ; j >= comp[i] ;  -- j )                   if ( fine[j] < fine[j-comp[i]] + value[i] )                        fine[j] = fine[j-comp[i]] + value[i] ;            printf ( "%d\n" , fine[total_comp] ) ;     }     return 0 ; }

Robberies

/*状态转移方程是:dp[j]=max(dp[j],dp[j-m[i]]*(1 - q[i]))  其中,dp[j]表示抢j块大洋的最大的逃脱概率,条件是dp[j-m[i]]可达,也就是之前抢劫过;始化为:dp[0]=1,其余初始化为-1.*/ #include<iostream>using namespace std;double max(double a,double b){    return a>b?a:b;}int main(){    int t;    cin>>t;    for(int x =1; x <= t; x++)    {        double p;        cin>>p;        int n;        cin>>n;        int i,j;        int* m = new int[n+1];        double * q = new double[n+1];        int  s = 0;        for(i = 1; i <= n; i++)        {            cin>>m[i]>>q[i];            s += m[i];        }        double *dp = new double[s+1];        dp[0] = 1;        for(i = 1; i <= n; i++)        for(j = s; j >= m[i]; j--)        {            dp[j] = max(dp[j],dp[j - m[i]]*(1-q[i]));        }        for(i = s; i >= 0; i--)        if(dp[i] >= 1-p)        {            cout<<i<<endl;            break;        }    }}

Watch The Movie

#include<cstdio>#include<cstring>#define max(a,b) a>b?a:b#define inf 0x7fffffff int N,M,L,t[105],v[105],dp[105][1005];int main(){    int T,i,j,k;    scanf("%d",&T);    while(T--)    {              //想买N部(即物品数量),最多卖M部(背包容量),最大时限为L              //每部耗时t[i],价值v[i]              //求在不超过最大时限L,且看完所有M部电影 的情况下获得的最大价值               //第一层背包:时间背包; 第二层背包:数量背包               scanf("%d%d%d",&N,&M,&L);              for(i=0;i<N;i++)                scanf("%d%d",&t[i],&v[i]);                                    //初始化dp                           for(i=0;i<=M;i++)                for(j=0;j<=L;j++)                {  if(i==0) dp[i][j]=0;                   else dp[i][j]=-inf;                 }                              //二维背包dp                                                      for(j=0;j<N;j++)  //注意枚举N个物品的变量必须放在最外层                  for(k=L;k>=t[j];k--)                     for(i=1;i<=M;i++)                      dp[i][k]=max(dp[i][k],dp[i-1][k-t[j]]+v[j]);               //判断                   if(dp[M][L]>0)printf("%d\n",dp[M][L]);              else  printf("0\n");                 }}

饭卡

/*这是一个变形的01背包问题,首先如果金额小于5元,剩余金额不变,为已有金额。如果大于等于5元我们先用5元买最贵的菜。然后用剩下的钱买其他的菜这时就是一个典型的01背包问题了;求出最大的花费,然后用总金额减去最大的花费即为剩余金额。抽象的地方是花费和价值都是用金额表示。*/#include <iostream> #include <stdio.h> #include <algorithm> #include <string.h> using namespace std; bool cmp(int a,int b) {     return a<b; } int main() {     int n,k[1001],f[1001],m;     while(scanf("%d",&n),n)     {         for(int i = 0; i < n; i++)         {             scanf("%d",&k[i]);         }         sort(k,k+n,cmp);         scanf("%d",&m);         if(m>=5)         {             memset(f,0,sizeof(f));             for(int i = 0; i < n-1; i++)             for(int j = m-5; j>=k[i];j--)             {                 if(f[j-k[i]]+k[i]>f[j]&&f[j-k[i]]+k[i]<=m-5)//注意这里的f[j-k[i]]+k[i]<=m-5。                 f[j] = f[j-k[i]]+k[i];             }             printf("%d\n",m-f[m-5]-k[n-1]);         }         else printf("%d\n",m);     }     return 0; }

Watch The Movie

#include<cstdio>#include<cstring>#define max(a,b) a>b?a:b#define inf 0x7fffffff int N,M,L,t[105],v[105],dp[105][1005];int main(){    int T,i,j,k;    scanf("%d",&T);    while(T--)    {              //想买N部(即物品数量),最多卖M部(背包容量),最大时限为L              //每部耗时t[i],价值v[i]              //求在不超过最大时限L,且看完所有M部电影 的情况下获得的最大价值               //第一层背包:时间背包; 第二层背包:数量背包               scanf("%d%d%d",&N,&M,&L);              for(i=0;i<N;i++)                scanf("%d%d",&t[i],&v[i]);                                    //初始化dp                           for(i=0;i<=M;i++)                for(j=0;j<=L;j++)                {  if(i==0) dp[i][j]=0;                   else dp[i][j]=-inf;                 }                              //二维背包dp                                                      for(j=0;j<N;j++)  //注意枚举N个物品的变量必须放在最外层                  for(k=L;k>=t[j];k--)                     for(i=1;i<=M;i++)                      dp[i][k]=max(dp[i][k],dp[i-1][k-t[j]]+v[j]);               //判断                   if(dp[M][L]>0)printf("%d\n",dp[M][L]);              else  printf("0\n");                 }}

Proud Merchants

/*按照q-p从小到大排序,然后01背包。至于按照q-p从小到大排序比较难想到。q-p其实就是不更新的范围,不更新的范围从小到大递增时就不会影响后面的DP了。*/#include<stdio.h>#include<string.h>#include<algorithm>#include<iostream>using namespace std;const int MAXN=550;struct Node{    int p,q,v;}node[MAXN];int dp[5500];bool cmp(Node a,Node b)//按照 q-p 从小到大排序{    return a.q-a.p < b.q-b.p;}int main(){    int n,m;    while(scanf("%d%d",&n,&m)!=EOF)    {        for(int i=0;i<n;i++)          scanf("%d%d%d",&node[i].p,&node[i].q,&node[i].v);        sort(node,node+n,cmp);        memset(dp,0,sizeof(dp));        for(int i=0;i<n;i++)          for(int j=m;j>=node[i].q;j--)            dp[j]=max(dp[j],dp[j-node[i].p]+node[i].v);        printf("%d\n",dp[m]);    }    return 0;}


原创粉丝点击