主頁 > 後端開發 > 背包問題(4):多重背包

背包問題(4):多重背包

2022-04-03 06:23:27 後端開發

        多重背包也是一種基本的背包問題模型,其基本特點是:每種物品有一個固定的裝入次數上限,

        多重背包問題的一般描述為:有N個物品,第i個物品的重量與價值分別為W[i]與P[i]且第i種物品最多有C[i] 件,背包容量為V,試問在每個物品不超過其上限的件數(物品必須保持完整)的情況下,如何讓背包裝入的物品具有更大的價值總和,

        其一般解題思路為:

        設f[i][j] 表示從編號1~i的物品中挑選任意數量的任意物品放入容量為j的背包中得到的最大價值,那么有  f[i][j]=max { f[i?1][j?k?w[i]]+k?v[i] ∣0≤k≤p[i]&k?w[i]≤j },

撰寫代碼時,可以寫成如下的回圈,

    for  ( i = 1; i <= N; i++)

         for  ( j = 1; j <= V; j++)

             for  (k = 0;  k <= C[i] &&  k * W[i] <= j; k++)

            {

                  f[i][j] = max( f[i][j], f[i - 1][j - k * W[i]] + k *P[i]);

             }

求得的最優值為f[N][V],

        同樣多重背包也可以進行空間優化,將二維陣列優化為一維陣列,即

              f[j]=max { f[j],f[j?k?W[i]]+k?P[i] }  (0≤k && k?W[i]≤j)

撰寫代碼時,一般采用如下的回圈,

    for (i=1; i<=N; i++)

       for (j=V; j>=0; j--)  

          for (k=0; k<=c[i] &&  k*W[i] <=j; k++)

              f[j] = max( f[j], f[j - k * W[i]] + k *P[i]);

求得的最優值為f[V],

        從上面的程式代碼可以看出,多重背包的處理一般采用三重回圈,時間復雜度較高,為了對時間進行優化,可以采用二進制優化法將多重背包轉變為0/1背包(采用二重回圈)進行處理,

        所謂二進制優化法就是將第 i 種c[i]件物品按二進制的方法分拆成s份“不同”的物品,例如,設第i件物品有20件,每件重量和價值分別為w和p,則可以分拆別5份“物品”如下:

第1份含有1件物品,重量為w,價值為p;

第2份含有2件物品,重量為2w,價值為2p;

第3份含有4件物品,重量為4w,價值為4p;

第4份含有8件物品,重量為8w,價值為8p;

第5份含有5件物品,重量為5w,價值為5p,

        因為,1+2+4+8+5=20,則由這5份“物品”可組合成0~20件之間任意件數的物品,這5份“物品”在進行組合時,每份物品要么選取,要么不選取,正好就是對這5份物品進行0/1背包處理,由此,多重背包可以通過這種方式轉化為0/1背包進行處理,從而將三重回圈優化為二重回圈處理,

        我們知道,k位二進制數可以表示0~2k-1之間的任意整數,k位二進制數從低位到高位,各位上的權值依次為1(20)、2(21)、4(22)、…、2k-1,因此,對于任意一個正整數x,則可以將其拆分為1+2+4+…+2k-1+y(其中y是二進制拆分剩余的部分,y<2k),

        通過拆分后,就可以將多重背包問題轉換為0/1背包問題求解了,

撰寫代碼如下:

         for (i=1; i<=N; i++)

        {

            int  s =C[i];

            for  (j=1; j<=s;  s-=j, j=j*2)                       // 二進制拆分

            {

                for  (k =V; k >=0 && k>= j*W[i]; k--)   // 0/1背包

                {

                    f[k] = max(f[k], f[k - j*W[i]] + j *P[i]);

                }

            }

            if (s > 0)                                               // 拆分的剩余部分

            {

                 for  ( j =V; j >= s * W[i]; j--)

                 {

                    f[j] = max(f[j], f[j - s * W[i]] + s * P[i]);

                 }

            }

        }

 【例1】購買糧食

問題描述

你準備自己采購一些糧食支援災區,現在假設你一共有資金n元,而市場有m種大米,每種大米都是袋裝產品,其重量和價格不等,并且只能整袋購買,

請問:你用有限的資金最多能采購多少公斤糧食呢?

輸入

輸入資料首先包含一個正整數C,表示有C組測驗用例,每組測驗用例的第一行是兩個整數n和m(1<=n<=100,1<=m<=100),分別表示經費的金額和大米的種類,然后是m行資料,每行包含3個數p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分別表示每袋的價格、每袋的重量以及對應種類大米的袋數,

輸出

對于每組測驗資料,請輸出能夠購買大米的最多重量,你可以假設經費買不光所有的大米,并且經費你可以不用完,每個實體的輸出占一行,

輸入樣例

1

8 2

2 100 4

4 100 2

輸出樣例

400

          (1)編程思路,

         典型的多重背包問題,按上面介紹的方法,采用二維陣列撰寫源程式1;采用一維陣列撰寫源程式2;采用二進制優化的方法撰寫源程式3,

         (2)采用二維陣列撰寫的源程式1,

#include <stdio.h>
#include <string.h>
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int p[105],h[105],c[105],f[105][105];
    int t;
    scanf("%d",&t);
    while (t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        int i,j,k;
        for (i=1;i<=m;i++)
            scanf("%d%d%d",&p[i],&h[i],&c[i]);
        memset(f,0,sizeof(f));
        for (i=1;i<=m;i++)
           for (j=1; j<=n; j++)
              for (k=0; k<=c[i] &&  k*p[i] <=j; k++)
                 f[i][j]=max(f[i][j],f[i-1][j-k*p[i]]+k*h[i]);
        printf("%d\n",f[m][n]);
    }
    return 0;
}

         (3)采用一維陣列撰寫的源程式2,

#include <stdio.h>
#include <string.h>
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int p[105],h[105],c[105],f[105];
    int t;
    scanf("%d",&t);
    while (t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        int i,j,k;
        for (i=1;i<=m;i++)
            scanf("%d%d%d",&p[i],&h[i],&c[i]);
        memset(f,0,sizeof(f));
        for (i=1;i<=m;i++)
            for (j=n;j>=0;j--)
                 for (k=0; k<=c[i] &&  k*p[i] <=j; k++)
                    f[j]=max(f[j],f[j-k*p[i]]+k*h[i]);
        printf("%d\n",f[n]);
    }
    return 0;
}

        (4)采用二進制優化的方法撰寫的源程式3,

#include <stdio.h>
#include <string.h>
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int p[105],h[105],c[105],f[105];
    int t;
    scanf("%d",&t);
    while (t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        int i,j,k;
        for (i=1;i<=m;i++)
            scanf("%d%d%d",&p[i],&h[i],&c[i]);
        memset(f,0,sizeof(f));
        for (i=1; i<=m; i++)
        {
            int  s =c[i];
            for  (j=1; j<=s;  s-=j, j=j*2)    // 二進制拆分
            {
                for  (k =n; k >=0 && k>= j*p[i]; k--)  // 0/1背包
                {
                    f[k] = max(f[k], f[k - j*p[i]] + j *h[i]);
                }
            }
            if (s > 0)          // 拆分的剩余部分
            {
                 for  ( j =n; j >= s * p[i]; j--)
                 {
                    f[j] = max(f[j], f[j - s * p[i]] + s * h[i]);
                 }
            }
        }
        printf("%d\n",f[n]);
    }
    return 0;
}

【例2】擺花

問題描述

小明的花店新開張,為了吸引顧客,他想在花店的門口擺上一排花,共m盆,通過調查顧客的喜好,小明列出了顧客最喜歡的n種花,從1到n標號,為了在門口展出更多種花,規定第i種花不能超過 ai盆,擺花時同一種花放在一起,且不同種類的花需按標號的從小到大的順序依次擺列,

試編程計算,一共有多少種不同的擺花方案,

輸入

第一行包含兩個正整數n和m(0<n≤100,0<m≤100),中間用一個空格隔開,

第二行有n個整數,每兩個整數之間用一個空格隔開,依次表示 a1,a2,…,an,

輸出

一個整數,表示有多少種方案,注意:因為方案數可能很多,請輸出方案數對 10^6+7取模的結果,

輸入樣例

2 4

3 2

輸出樣例

2

         (1)編程思路,

        典型的多重背包問題,按前面的介紹進行處理即可,

        采用二維陣列,定義int f[105][105]={0},設f[i][j]表示前i種花中擺放j盆花的方案數,初始值初f[0][0]=1(什么也不擺)外,其余全部為0,可以撰寫如下的源程式1,

        采用一維陣列,int f[105]={0};設f[i]表示擺放i盆花的方案數,可以撰寫如下的源程式2,

        (2)使用二維陣列撰寫的源程式1,

#include <stdio.h>
int main()
{
    int f[105][105]={0},a[105];
    int n,m;
    scanf("%d%d",&n,&m);
    int i,j,k;
    for (i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
    }
    f[0][0]=1;
    for (i=1; i<=n; i++)
       for (j=0; j<=m; j++)
          for (k=0; k<=(a[i]<j?a[i]:j); k++)
              f[i][j] = (f[i][j] + f[i-1][j-k])%1000007;
    printf("%d\n",f[n][m]);
    return 0;
}

         (3)使用一維陣列撰寫的源程式2,

#include <stdio.h>
int main()
{
    int f[105]={0},a[105];
    int n,m;
    scanf("%d%d",&n,&m);
    int i,j,k;
    for (i=1; i<=n; i++)
        scanf("%d",&a[i]);
    f[0] = 1;
    for (i=1; i<=n; i++)
       for (j=m; j>=0; j--)
          for (k=1; k<=(a[i]<j?a[i]:j); k++)
              f[j] = (f[j] + f[j-k])%1000007;
    printf("%d\n",f[m]);
    return 0;
}

        將上面的源程式1和2提交給洛谷題庫P1077 [NOIP2012 普及組] 擺花(https://www.luogu.com.cn/problem/P1077),測評結果均為Accepted

 【例3】收集櫻花

問題描述

有k棵櫻花樹,在第i棵樹下最多能收集到 si朵櫻花(收集了0朵櫻花也算收集了櫻花),

你有多少種方案能夠收集到恰好n朵櫻花呢?

輸入

第一行兩個正整數 n,k,表示要收集n朵櫻花,有k棵櫻花樹,

接下來一行k個正整數 s1,s2,…,sk,其中 si表示最多在第i棵櫻花樹下收集到si朵櫻花,

輸出

一行一個整數,表示恰好收集到n朵櫻花的方案數,

由于答案可能太大,請輸出答案對 10086001 取模后的值,

特殊地,如果收集不到n朵櫻花,請輸出一個字串 impossible,

輸入樣例#1

3 4

1 1 1 1

輸出樣例 #1

5

輸入樣例 #2

10 9

9 6 8 7 9 6 5 4 3

輸出樣例#2

68345

輸入樣例 #3

10 5

2 2 2 2 1

輸出樣例 #3

Impossible

        (1)編程思路1,

        定義二維陣列int f[5001][5001];其中f[i][j]表示前i顆櫻花樹下共收集到j朵櫻花的方案數,顯然,有f[i][0]=1(1≤i≤n,每顆樹下可不收集櫻花,收集了0朵櫻花也算收集了櫻花,方案數為1),還有f[1][j]=1(1≤j≤s[1],第1顆櫻花樹下可分別收集1~s[1]朵櫻花),

        轉移方程為: f[i][j]=f[i][j]+f[i-1][j-k]  (其中k值為第i可櫻花樹收集櫻花的朵數),

        (2)源程式1,

#include <stdio.h>
#include <string.h>
int f[5001][5001]={0};
int main()
{
    int v,n;
    scanf("%d%d",&v,&n);
    int s[5001];
    int i,j,k;
    int tot=0;
    for (i=1;i<=n;i++)
    {
        scanf("%d",&s[i]);
        tot+=s[i];
    }
    if (tot<v)
    {
        printf("impossible\n");
        return 0;
    }
    for (i=1;i<=n;i++)
        f[i][0]=1;
    for (i=1;i<=s[1];i++)
        f[1][i]=1;
    for (i=2; i<=n; i++)
    {
        for (j=1; j<=v; j++)
        {
             for  (k=0;  k<=s[i] &&  k<=j; k++)
             {
                  f[i][j]=(f[i][j]+f[i-1][j-k])%10086001;
             }
        }
    }
    int ans=0;
    for (i=1; i<=n; i++)
        ans = (ans+f[i][v])%10086001;
    printf("%d\n",ans);
    return 0;
}

        將上面的源程式1提交給洛谷題庫P6394 櫻花,還有你(https://www.luogu.com.cn/problem/P6394),測評結果為Unaccepted,其中測驗點#17、測驗點#19和測驗點#20,顯示TLE,測驗點#18顯示MLE

         (3)編程思路2,

        由于資料量較大,因此采用二維陣列存盤,會存在超記憶體限制的情況,因此采用一維陣列進行處理,

        (4)源程式2,

#include <stdio.h>
#include <string.h>
int f[5001]={0};
int main()
{
    int v,n;
    scanf("%d%d",&v,&n);
    int s[5001];
    int i,j,k;
    int tot=0;
    for (i=1;i<=n;i++)
    {
        scanf("%d",&s[i]);
        tot+=s[i];
    }
    if (tot<v)
    {
        printf("impossible\n");
        return 0;
    }
    f[0]=1;
    int ans=0;
    for (i=1;i<=n;i++)        //  前i棵樹
    {
        for (j=v;j>=0;j--)   
            for (k=1;k<=s[i] && k<=j;k++)
                 f[j]=(f[j]+f[j-k])%10086001;
        ans=(ans+f[v])%10086001;
    }
    printf("%d\n",ans);
    return 0;
}

        將上面的源程式2提交給洛谷題庫P6394 櫻花,還有你(https://www.luogu.com.cn/problem/P6394),測評結果仍為Unaccepted,其中測驗點#17、測驗點#18、測驗點#19和測驗點#20,均顯示TLE

        (5)編程思路3,

         源程式2中的多重背包采用三重回圈實作,由于資料量較大,回圈處理太耗時,因此會超時,觀察第三層回圈,每次的f[j]都是加上一個區間,所以可以直接用一個前綴和來計算,這樣第三層回圈就可以省掉了,

        (6)源程式3,

#include <stdio.h>
int main()
{
    int v,n;
    scanf("%d%d",&v,&n);
    int f[5001]={0};
    int s[5001];
    int sum[5001]={0};  // 保存前綴和
    int i,j;
    int tot=0;
    for (i=1;i<=n;i++)
    {
        scanf("%d",&s[i]);
        tot+=s[i];
    }
    if (tot<v)
    {
        printf("impossible\n");
        return 0;
    }
    sum[0]=f[0]=1;
    int ans=0;
    for (i=1;i<=n;i++)        // 前i棵樹
    {
        for (j=1;j<=v;j++)    // 更新前綴和
           sum[j]=(sum[j-1]+f[j])% 10086001;
        for (j=v;j>=1;j--)   
        {
            int k=s[i]<j?s[i]:j;
            if (k==j) f[j]=(f[j]+sum[j-1])% 10086001;
            else f[j]=(f[j]+sum[j-1]-sum[j-k-1]+10086001)% 10086001;  //利用前綴和
        }
        ans=(ans+f[v])%10086001;
    }
    printf("%d\n",ans);
    return 0;
}

        將上面的源程式3提交給洛谷題庫P6394 櫻花,還有你(https://www.luogu.com.cn/problem/P6394),測評結果為Accepted

 【例4】砝碼稱重

問題描述

設有1g、2g、3g、5g、10g、20g的砝碼各若干枚(其總重≤1000),計算用這些砝碼能稱出的不同重量的個數N,但不包括一個砝碼也不用的情況,

輸入

輸入方式:a1 , a2 ,a3 , a4 , a5 ,a6(表示1g砝碼有a1個,2g砝碼有a2個,…,20g砝碼有a6個),

輸出

輸出方式:Total=N

輸入樣例

1 1 0 0 0 0

輸出樣例

Total=3

          (1)編程思路,

         設f[i]表示重量i是否可以稱出,初始時,f[0]=1,其余全部為0,輸入6種砝碼的個數w[i],計算出所有砝碼全部使用時,可稱出的總重量tot,這也是背包的容量,

        將6種砝碼按多重背包的處理方法加入背包,若重量j-w[i]*k可稱出,即f[j-w[i]*k]==1,則加上k個重w[i]的砝碼后,重量j也可以稱出,置f[j]=1,

        多重背包處理完后,f[1]~f[tot]元素值為1的個數就是可稱出的不同重量的個數,

        (2)源程式,

#include <stdio.h>
int main()
{
    int f[1001]={0};
    int w[7]={0,1,2,3,5,10,20};
    int a[7];
    int i,j,k;
    int tot=0;   // 總重量
    for (i=1;i<=6;i++)
    {
        scanf("%d",&a[i]);
        tot+=w[i]*a[i];
    }
    f[0]=1;
    for (i=1;i<=6;i++)
    {
          for (j=tot;j>=0;j--)
              for (k=0;k<=a[i];k++)
              {
                   if (j-w[i]*k>=0 && f[j-w[i]*k]!=0)
                    f[j]=1;
            }
    }
    int ans=0;
    for (i=1;i<=tot;i++)
       if (f[i]!=0) ans++;
    printf("Total=%d\n",ans);
  return 0;
}

        將上面的源程式提交給洛谷題庫P2347 [NOIP1996 提高組] 砝碼稱重(https://www.luogu.com.cn/problem/P2347),測評結果為Accepted

 【例5】買表

問題描述

Jimmy 到 Symbol 的手表店買手表,Jimmy 只帶了n種錢幣,第i種錢幣的面額為 ki元,張數為 ai張,Symbol 的店里一共有m 塊手表,第i 塊手表的價格為 ti元,

Symbol 的手表店不能找零,所以 Jimmy 只能在湊出恰好的錢數時才能購買一塊手表,現在對于店里的每塊手表,Jimmy 想知道他能不能湊出恰好的錢數進行購買,

輸入

第一行兩個空格分隔的整數 n(1≤n≤200)和 m(1≤m≤100000) 表示錢幣數與手表數,

接下來 n 行每行兩個空格分隔的整數 ki(1≤ki≤500000)和 ai(1≤ai≤1000)表示錢幣的面額和張數,

第 n+2行,共 m個用空格分隔的整數 ti(0≤ti≤500000),表示每塊手表的價格,

輸出

一共m 行,對于第i 行,如果能湊出恰好的錢數購買第i 塊手表則輸出 Yes 否則輸出 No,注意只有首字母大寫,

輸入樣例

3 5

1 2

5 1

6 3

3 19 21 1 7

輸出樣例

No

Yes

No

Yes

Yes

         (1)編程思路,

        設f[i]表示錢數i是否可以用n種錢幣湊出,初始時,f[0]=1,其余全部為0,由于每種錢幣的張數較多(最多可達1000張),因此按照前面介紹的二進制數優化方法,將多重背包變化成0/1背包進行處理,

        (2)源程式,

#include <stdio.h>
#include <string.h>
int main()
{
    int f[500005]={0},w[2005];
    int n,m;
    scanf("%d%d",&n,&m);
    int i,j;
    int cnt=0;
    for (i=1; i<=n; i++)
    {
        int k,a;
        scanf("%d%d",&k,&a);
        for (j=1; j<=a; j*=2)  // 多重背包轉成0/1背包
        {
            w[++cnt]=j*k;
            a-=j;
        }
        if (a>0)
        {
            w[++cnt]=k*a;
        }
    }
    f[0]=1;
    for (i=1; i<=cnt; i++)      // 01背包的求解
        for(j=500000; j>=w[i]; j--)
            if (f[j-w[i]]) f[j]=1;
    for (i=1;i<=m;i++)
    {
        int t;
        scanf("%d",&t);
        if (f[t]) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

        將上面的源程式提交給洛谷題庫P6567 [NOI Online #3 入門組] 買表(https://www.luogu.com.cn/problem/P1776),測評結果為Accepted

 【例6】英雄聯盟

問題描述

正在上大學的小皮球熱愛英雄聯盟這款游戲,而且打的很菜,被網友們戲稱為「小學生」,

現在,小皮球終于受不了網友們的嘲諷,決定變強了,他變強的方法就是:買皮膚!

小皮球只會玩N個(5≤N≤150)英雄,因此,他也只準備給這N個英雄買皮膚,并且決定,以后只玩有皮膚的英雄,

這N個英雄中,第i個英雄有Ki 款皮膚,價格是每款 Ci個Q 幣(同一個英雄的皮膚價格相同),

為了讓自己看起來高大上一些,小皮球決定給同學們展示一下自己的皮膚,展示的思路是這樣的:對于有皮膚的每一個英雄,隨便選一個皮膚給同學看,

比如,小皮球共有 5 個英雄,這 5 個英雄分別有0,0,3,2,4 款皮膚,那么,小皮球就有3×2×4=24 種展示的策略,

現在,小皮球希望自己的展示策略能夠至少達到M (M≤1017)種,請問,小皮球至少要花多少錢呢?

輸入

第一行,兩個整數N,M,

第二行,N個整數,表示每個英雄的皮膚數量Ki,

第三行,N 個整數,表示每個英雄皮膚的價格Ci ,

輸出

一個整數,表示小皮球達到目標最少的花費,

輸入樣例

3 24

4 4 4

2 2 2

輸出樣例

18

         (1)編程思路,

         展示方案達到M種作為背包的容量,每一個英雄都有一個皮膚數量、一個購買的Q幣數(花費),將每個英雄的皮膚看成物品,就是有限物品數量的多重背包問題,

         設f[j]表示花費j個Q幣購買英雄皮膚可得到的最大展示方案數量,則狀態轉移方程為:

         f[j]=max(f[j?p?c[i]]?p,f[j]),其中 p是當前英雄所選的皮膚數量,c[i]該皮膚的購買價格,

         多重背包處理完后,用變數i從0~tot搜索f[i]的判斷,第1次遇到的 f[i]>=m的i值就是所求的最小花費,

          (2)源程式,

#include <stdio.h>
long long max(long long a,long long b)
{
    return a>b?a:b;
}
long long f[250000]={0};
int main()
{
    int n;
    long long m;
    scanf("%d%lld",&n,&m);
    int i,j,p;
    int k[155],c[155];
    for (i=1;i<=n;i++)
        scanf("%d",&k[i]);
    int tot=0;   // 花的總錢數
    for (i=1;i<=n;i++)
    {
        scanf("%d",&c[i]);
        tot+=c[i]*k[i];
    }
    f[0]=1;
    for (i=1;i<=n;i++)
    {
          for (j=tot;j>=0;j--)
              for (p=0;p<=k[i];p++)
              {
                   if (j-c[i]*p>=0)
                    f[j]=max(f[j],1L*f[j-c[i]*p]*p);
            }
  }
  for (i=0;i<=tot;i++)
    if (f[i]>=m)
    {
       printf("%d\n",i);
       break;
    }
  return 0;
}

         將上面的源程式提交給洛谷題庫P5365 [SNOI2017]英雄聯盟(https://www.luogu.com.cn/problem/P5365),測評結果為“Accepted

 【例7】寶物篩選

問題描述

小F找到了王室的寶物庫,里面堆滿了無數價值連城的寶物,但是這里的寶物實在是太多了,小F的采集車似乎裝不下那么多寶物,看來小F只能含淚舍棄其中的一部分寶物了,

小F對庫里的寶物進行了整理,他發現每樣寶物都有一件或者多件,他粗略估算了下每樣寶物的價值,之后開始了寶物篩選作業:小F有一個最大載重為W的采集車,寶物庫里總共有n種寶物,每種寶物的價值為vi,重量為wi,每種寶物有mi件,小F希望在采集車不超載的前提下,選擇一些寶物裝進采集車,使得它們的價值和最大,

輸入

第一行為一個整數n(1≤n≤100)和W(0≤W≤40000),分別表示寶物種數和采集車的最大載重,

接下來n行每行三個整數vi,wi,mi,n≤∑mi ≤100000,

輸出

輸出僅一個整數,表示在采集車不超載的情況下收集的寶物的最大價值,

輸入樣例

4 20

3 9 3

5 9 1

9 4 2

8 1 3

輸出樣例

47

         (1)編程思路1,

        典型的多重背包問題,按前面介紹的采用一維陣列用三重回圈撰寫源程式1,

       (2)采用一維陣列用三重回圈撰寫的源程式1,

#include <stdio.h>
#include <string.h>
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int n,limw;
    scanf("%d%d",&n,&limw);
    int i,j,k;
    int f[40005];
    memset(f,0,sizeof(f));
    for (i=1;i<=n;i++)
    {
        int v,w,m;
        scanf("%d%d%d",&v,&w,&m);
        for (j=limw;j>=0;j--)
           for (k=0; k<=m &&  k*w<=j; k++)
              f[j]=max(f[j],f[j-k*w]+k*v);
    }
    printf("%d\n",f[limw]);
    return 0;
}

         將上面的源程式提交給洛谷題庫P1776 寶物篩選(https://www.luogu.com.cn/problem/P1776),測評結果為Unaccepted,其中測驗點#4~測驗點#10,均顯示TLE

        (3)編程思路2,

        采用一維陣列用三重回圈撰寫的源程式1顯示超時了,為了進行時間優化,采用二進制拆分優化法將多重背包轉換為0/1背包進行處理,撰寫下面的源程式2,

        (4)采用二進制拆分優化法撰寫的源程式2,

#include <stdio.h>
#include <string.h>
int main()
{
    int n,limw;
    scanf("%d%d",&n,&limw);
    int v[100005],w[100005];
    int i,j;
    int cnt=0;
    for (i=1;i<=n;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        for (j=1;j<=c;j<<=1)
        {
            v[++cnt]=j*a;
            w[cnt]=j*b;
            c-=j;
        }
        if (c)
        {
            v[++cnt]=a*c;
            w[cnt]=b*c;
        }
    }
    int f[40005];
    memset(f,0,sizeof(f));
    for (i=1;i<=cnt;i++)        //  轉換為cnt種寶物
        for (j=limw;j>=w[i];j--)
            if (f[j]<f[j-w[i]]+v[i]) f[j]=f[j-w[i]]+v[i];
    printf("%d\n",f[limw]);
    return 0;
}

        將上面的源程式提交給洛谷題庫P1776 寶物篩選(https://www.luogu.com.cn/problem/P1776),測評結果為Accepted

 【例8】科技莊園

問題描述

Life種了一塊田,里面種了有一些桃樹,

Life對PFT說:“我給你一定的時間去摘桃,你必須在規定的時間之內回到我面前,否則你摘的桃都要歸我吃!”

PFT思考了一會,最終答應了!

由于PFT的數學不好!它并不知道怎樣才能在規定的時間獲得最大的價值,

由于PFT不是機器人,所以他的體力并不是無限的,他不想摘很多的桃以至體力為0,而白白把桃給Life,同時PFT每次只能摘一棵桃樹,每棵桃樹都可以摘K次(對于同一棵桃每次摘的桃數相同),每次摘完后都要回傳出發點(PFT一次拿不了很多)即Life的所在地(0,0),設試驗田左上角的桃樹坐標是(1,1),

PFT每秒只能移動一個單位,每移動一個單位耗費體力1(摘取不花費時間和體力,但只限上下左右移動),

輸入

第一行:四個數為N,M,TI,A(10≤N,M,TI,A≤100)分別表示試驗田的長和寬,Life給PFT的時間,和PFT的體力,

下面一個N行M列的矩陣桃田,表示每次每棵桃樹上能摘的桃數,

接下來N行M列的矩陣,表示每棵桃最多可以采摘的次數K,

輸出

一個數:PFT可以獲得的最大的桃個數,

輸入樣例

4 4 13 20

10 0  0  0

0  0  10 0

0  0  10 0

0  0  0  0

1 0 0 0

0 0 2 0

0 0 4 0

0 0 0 0

輸出樣例

10

         (1)編程思路,

        定義陣列int dist[10005],num1[10005],num2[10005]; ,分別用于保存每顆桃樹到(0,0)的距離、樹上的桃子數、可采摘的次數,

        在輸入后進行預處理,每塊桃樹田中把能摘到桃子的(桃子數量>0)并且可采摘次數>0桃樹相關資訊分別保存到dist、num1和num2這三個陣列中,

        這樣,陣列中的每顆桃樹可以看成一件物品,問題轉變為給定體力V、桃樹數量N(能摘到桃子的)、每顆桃樹最多被采摘K次,在這種情況下最多能摘多少桃子,就是一個典型的多重背包問題了,

        背包容量為V,V受兩個條件約束,Life給PFT的時間TI和PFT的體力A,Life給PFT的時間可以轉換為體力,因為PFT每秒只能移動一個單位,每移動一個單位耗費體力1,因此給定時間TI就是可供PFT消耗的體力,另外PFT的體力是他實際擁有的體力,FPT最后回去時,體力不能為0,因此至少得留一格體力,這樣可確定背包容量V取TI和A-1中的較小值,

        (2)源程式,

#include <stdio.h>
long long max(long long a,long long b)
{
    return a>b?a:b;
}
int main()
{
    int n,m,x,y,v;
    scanf("%d%d%d%d",&n,&m,&x,&y);
    v=x<(y-1)?x:y-1;
    int i,j,k;
    int map[105][105];
    long long f[105]={0};
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)
            scanf("%d",&map[i][j]);
    int dist[10005],num1[10005],num2[10005]; // 分別表示桃樹的距離、桃子數、可采摘的次數
    int cnt=0;
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)
        {
            int a;
            scanf("%d",&a);
            if (a>0 && map[i][j]>0)
            {
                dist[cnt]=2*(i+j);
                num1[cnt]=map[i][j];
                num2[cnt]=a;
                cnt++;
            }
        }
    for (i=0;i<cnt;i++)
        for (j=v;j>=0;j--)
            for (k=1;k<=num2[i] && k*dist[i]<=j;k++)
                f[j]=max(f[j],f[j-k*dist[i]]+k*num1[i]);
    printf("%lld\n",f[v]);
    return 0;
}

        將上面的源程式提交給洛谷題庫P2760 科技莊園(https://www.luogu.com.cn/problem/P2760),測評結果為Accepted

 【例9】硬幣

問題描述

有面值分別為A1、A2、…、An的n種硬幣,每種硬幣各有C1、C2、…、Cn枚,用這些硬幣可以組合成多少種不同的不超過m的錢數,

輸入                

輸入包含幾個測驗用例,每個測驗用例的第一行包含兩個整數n(1<=n<=100),m(m<=100000),第二行包含2n個整數,表示A1,A2,…,An,C1,C2,…,Cn(1<=Ai<=100000,1<=Ci<=1000),最后一個測驗用例后跟兩個零,

輸出

對于每個測驗用例,在一行上輸出答案,

輸入樣例

3 10

1 2 4 2 1 1

2 5

1 4 2 1

0 0

輸出樣例

8

4

        (1)編程思路1,

        n種硬幣,設第i種硬幣的面值為Ai,數量為Ci,將這些硬幣裝入容量為m的背包中,求這些硬幣能夠組成從1到m中的哪些數字,

        多重背包問題,可采用二進制拆分優化的方法撰寫如下的源程式1.

?        (2)源程式1,

#include <stdio.h>
#include <string.h>
int main()
{
    int a[101], c[101],w[101];
    int f[100005];
    int n, m;
    while (scanf("%d%d",&n,&m) && (n||m))
    {
        int i,j;
        for (i=1; i<=n; i++)
           scanf("%d",&a[i]);
        for (i=1; i<=n; i++)
           scanf("%d",&c[i]);
       memset(f,0,sizeof f);
       f[0] = 1;
       int ans = 0;
       for (i=1; i<=n; i++)
       {
           int k,cnt=0;
           for (j=1; j<=c[i]; j*=2)  // 多重背包轉成0/1背包
           {
              w[++cnt]=j*a[i];
              c[i]-=j;
           }
           if (c[i]>0)
           {
              w[++cnt]=c[i]*a[i];
           }
           for (j=1; j<=cnt; j++)
              for(k=m; k>=w[j]; k--)
                 if (!f[k] && f[k -w[j]])
                 {
                     f[k] = 1;
                     ans++;
                 }
       }
       printf("%d\n",ans);
   }
   return 0;
}

         將上面的源程式1提交給北大OJ題庫POJ 1742 Coins(http://poj.org/problem?id=1742),測評結果為Time Limit Exceeded,超時,

        (3)編程思路2,

        可以這樣來考慮問題,

        對于第i種硬幣,如果A i ? C i≥m,相當于Ai取的個數沒有限制,即可以把第i種硬幣的數量視為無窮,作為一個完全背包來求解;否則,就作為一個多重背包來求解,

        (4)源程式2,

#include <stdio.h>
#include <string.h>
int main()
{
    int a[101], c[101],w[101];
    int f[100005];
    int n, m;
    while (scanf("%d%d",&n,&m) && (n||m))
    {
        int i,j;
        for (i=1; i<=n; i++)
           scanf("%d",&a[i]);
        for (i=1; i<=n; i++)
           scanf("%d",&c[i]);
       memset(f,0,sizeof f);
       f[0] = 1;
       int ans = 0;
       for (i=1; i<=n; i++)
       {
           if (a[i] * c[i]>= m)
           {
               for (j=a[i]; j<=m; j++)          // 完全背包
                   if (!f[j] && f[j - a[i]])
                   {
                       f[j] = 1;
                       ans++;
                   }
           }
           else
           {
               int k,cnt=0;
               for (j=1; j<=c[i]; j*=2)      // 多重背包轉成0/1背包
               {
                  w[++cnt]=j*a[i];
                  c[i]-=j;
               }
               if (c[i]>0)
               {
                  w[++cnt]=c[i]*a[i];
               }
               for (j=1; j<=cnt; j++)
                  for(k=m; k>=w[j]; k--)
                     if (!f[k] && f[k -w[j]])
                     {
                         f[k] = 1;
                         ans++;
                     }
           }
       }
       printf("%d\n",ans);
   }
   return 0;
}

        將上面的源程式2提交給北大OJ題庫POJ 1742 Coins(http://poj.org/problem?id=1742),測評結果為Accepted

        (5)編程思路3,

        也可以這樣考慮問題,

        定義陣列int f[100010],其中f[i]表示容量為i的背包是否可以裝滿,即錢數i是否可以構成,f[i]=1表示i可以構成,初始化時,將陣列f全部設為0,f[ 0 ]設為1,

        利用雙重回圈,外回圈i從0到n-1遍歷每種硬幣A[ i ],內回圈 j 從A[i]開始往后遍歷到背包容量m,只要f[ j-A[i] ]==1(即表示錢數j-A[i]能夠構成),且f[j]==0(表示錢數j 尚未被構成),則錢數 j 是有可能構成的,

        為什么說有可能呢?是因為能否構成錢數 j,還得看A[i]的數量是否達到上限C[i],

        為了記錄硬幣A[i]的使用數量,定義一個專門記錄個數的陣列sum[M],在第一層回圈內將陣列sum[ ]初始化為0,一旦滿足f[j -A[i] ] && ! f[ j ] && num[ j - A[ i ] ]<C[i] ,則說明錢數j是可以構成的,則將f[ j ]的值置為1,再將sum[ j ]=num[ j - A[i ] ]+1 表示構成錢數j所對應的面值為A[ i ]的硬幣的使用數在錢數為 j-A[ i ]的基礎上加1,有了這個sum陣列,可以保證硬幣A[i]的使用次數不會超過C[i],

        (6)源程式3,

#include <stdio.h>
#include <string.h>
int f[100010], sum[100010];
int main()
{
    int n,m,i,j,cnt;
    int a[101],c[101];
    while (scanf("%d%d",&n,&m) && n!=0 && m!=0)
    {
       for (i=0;i<n;i++)
           scanf("%d",&a[i]);
       for (i=0;i<n;i++)
          scanf("%d",&c[i]);
       memset(f, 0, sizeof(f));
       f[0] = 1;
       cnt = 0;
       for (i=0; i<n; i++)
       {
            memset(sum, 0, sizeof(sum));
            for (j=a[i]; j<= m; j++)
            {
                if (!f[j] && f[j - a[i]] && sum[j - a[i]] < c[i])
                {
                    f[j] = 1;
                    sum[j] = sum[j-a[i]] + 1;
                    cnt++;
                }
            }
        }
        printf("%d\n",cnt);
    }
    return 0;
}

        將上面的源程式3提交給北大OJ題庫POJ 1742 Coins(http://poj.org/problem?id=1742),測評結果為Accepted

 【例10】咖啡自動售歡訓

問題描述

某咖啡自動售歡訓只接收面值為1美分、5美分、10美分和25美分的硬幣,給定你手里擁有的這4種硬幣中每種硬幣的數量以及咖啡價格,請給出支付咖啡時必須使用的硬幣,要求你使用盡可能多的硬幣個數,并且不能找零,

輸入

輸入包括多組測驗用例,每個測驗用例用一行輸入,

每行輸入包含五個整數,每個數用一個空格分隔,描述一種需要解決的情況,第1個整數為P(1<=P<=10000),是以美分為單位的咖啡價格,接下來的四個整數,C1,C2,C3,C4(0<=Ci<=10000),是1美分、5美分、10美分和25美分的硬幣個數,輸入的最后一行包含五個零,作為測驗用例的結束,

輸出

對于每種情況,都應該輸出一行,其中包含字串“Throw in T1 cents、T2 nickels、T3 dimes和T4 quarters”,其中T1、T2、T3、T4是在使用盡可能多的硬幣的同時,應該使用適當價值的硬幣來支付咖啡,如沒有足夠的零錢來支付咖啡的價格,程式應該輸出“Charlie cannot buy coffee.”,

輸入樣例

12 5 3 1 2

16 0 0 0 1

0 0 0 0 0

輸出樣例

Throw in 2 cents, 2 nickels, 0 dimes, and 0 quarters.

Charlie cannot buy coffee.

        (1)編程思路,

        4種硬幣,面值分別為V[1]、V[2]、V[3]、V[4],每種硬幣的個數分別為C[1]、C[2]、C[3]、C[4]用這4種硬幣來組成咖啡的支付價格P,典型的多重背包問題,

        本題在使用多重背包求解時,有兩個關鍵點,

        1)在一般的背包問題里,有容量、價值,所要求的不是最大價值就是最小價值,而定義的f[i][j]表示裝有i件物品,容量為j的最大值為f[i][j],簡化為一維的f[j]表示的是在容量為j的時候最大價值為f[j],在狀態轉移時,f[j]可以從f[j-v[i]]那邊得到值,但它并不需要去判斷在f[j-v[i]]前面是否有數可以組成f[j-v[i]],因此f[j-v[i]]為不為0,不影響最終結果,本題中,f[j]表示構成支付價格j時需要使用的最多硬幣數,在狀態轉移的時候要保證f[j-v[i]]>0(初始時,f[0]=1,其余元素全為0),這是因為題意是要求組成P分錢需要的最多硬幣數量,這樣要可以組成f[j],f[j-v[i]]必須要可以組成,否則就會出錯,同時,還要保證f[j-v[i]]+1>f[j](用了面值為V[i]的硬幣后,使用的硬幣數得多一些才行)且硬幣的使用個數不超過c[i](可參考上面例9的編程思路3),

        2)要求出各種硬幣使用多少個,需要記錄路徑,為此定義陣列path[10010],在狀態由f[j-v[i]]到f[j]轉移時,記錄path[j]=j-v[i],這樣,j-path[j]=j-(j-v[i])=v[i],而v[i]正好是硬幣的面值,可以對相應面值的硬幣使用個數進行計數,

        (2)源程式,

#include <stdio.h>
#include <string.h>
int main()
{
    int f[10010],ans[10010],num[10010],path[10010];
    int c[5]={0,1,5,10,25};
    int t[5],p;
    while (scanf("%d%d%d%d%d",&p,&t[1],&t[2],&t[3],&t[4])&&(p||t[1]||t[2]||t[3]||t[4]))
    {
        memset(f,0,sizeof(f));
        memset(ans,0,sizeof(ans));
        memset(path,0,sizeof(path));
        f[0]=1;
        int i,j;
        for (i=1;i<=4;i++)
        {
            memset(num,0,sizeof(num));
            for (j=c[i];j<=p;j++)
                if (f[j-c[i]] && f[j-c[i]]+1>f[j] && num[j-c[i]]<t[i])
                {
                    f[j]=f[j-c[i]]+1;
                    num[j]=num[j-c[i]]+1;
                    path[j]=j-c[i];
                }
        }
        if (f[p]>0)
        {
            i=p;
            while(i!=0)
            {
                ans[i-path[i]]++;
                i=path[i];
            }
            printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[1],ans[5],ans[10],ans[25]);
        }
        else
            printf("Charlie cannot buy coffee.\n");
    }
    return 0;
}

         將上面的源程式提交給北大OJ題庫POJ 1787 Charlie's Change(http://poj.org/problem?id=1787),可以Accepted, 

練習題

1.P6771 [USACO05MAR]Space Elevator 太空電梯(https://www.luogu.com.cn/problem/P6771

#include <stdio.h>
#include <string.h>
struct Node
{
    int h,a,c;
};
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int k;
    scanf("%d",&k);
    struct Node block[405];
    int f[40500]={0};
    int i,j;
    for (i=1;i<=k;i++)
    {
        scanf("%d%d%d",&block[i].h,&block[i].a,&block[i].c);
    }
    for (i=1;i<k;i++)
        for (j=1;j<=k-i;j++)
            if (block[j].a>block[j+1].a)
            {
                struct Node temp;
                temp=block[j]; block[j]=block[j+1]; block[j+1]=temp;
            }
    for (i=1;i<=k;i++)
    {
        int n;
        for (n=1;n<=block[i].c;n++)     //多重背包
        {
            for (j=block[i].a;j>=block[i].h;j--)
            {
                f[j]=max(f[j],f[j-block[i].h]+block[i].h);
            }
        }
    }
    int ans=0;
    for (i=1;i<=block[k].a;i++)
        ans=max(ans,f[i]);
    printf("%d\n",ans);
    return 0;
}
參考源程式1
#include <stdio.h>
#include <string.h>
struct Node
{
    int h,a,c;
};
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int k;
    scanf("%d",&k);
    struct Node block[405];
    int f[40500]={0};
    int i,j;
    for (i=1;i<=k;i++)
    {
        scanf("%d%d%d",&block[i].h,&block[i].a,&block[i].c);
    }
    for (i=1;i<k;i++)
        for (j=1;j<=k-i;j++)
            if (block[j].a>block[j+1].a)
            {
                struct Node temp;
                temp=block[j]; block[j]=block[j+1]; block[j+1]=temp;
            }
    for (i=1;i<=k;i++)
    {
        if (block[i].a>block[i].c*block[i].h)
        {
            int n;
            for (n=1;n<block[i].c;n*=2)
            {
                for (j=block[i].a;j>=block[i].h*n;j--)
                {
                    f[j]=max(f[j],f[j-block[i].h*n]+block[i].h*n);
                }
                block[i].c-=n;
            }
            if (block[i].c>0)
            {
                for (j=block[i].a;j>=block[i].h*block[i].c;j--)
                f[j]=max(f[j],f[j-block[i].h*block[i].c]+block[i].h*block[i].c);
            }
        }
        else
        {
            for (j=block[i].h;j<=block[i].a;j++)
            {
                f[j]=max(f[j],f[j-block[i].h]+block[i].h);
            }
        }
    }
    int ans=0;
    for (i=1;i<=block[k].a;i++)
        ans=max(ans,f[i]);
    printf("%d\n",ans);
    return 0;
}
參考源程式2

2.POJ 1014 Dividing(http://poj.org/problem?id=1014

#include <stdio.h>
#define INF 100000000
int f[240005];
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int test=1,c[7],w[1001],n[7];
    while (1)
    {
        int i,j;
        for (i=1;i<=6;i++)
            scanf("%d",&n[i]);
        if (n[1]==0 && n[2]==0 && n[3]==0 && n[4]==0 && n[5]==0 && n[6]==0)
            break;
        int SumValue=https://www.cnblogs.com/cs-whut/p/0;
        for (i=1;i<=6;i++)
        {
            c[i] = i;
            SumValue+=i*n[i];
        }
        printf("Collection #%d:\n",test++);
        if (SumValue%2)    // 總價值為奇數,無法平分
        {
            printf("Can't be divided.\n\n");
        }
        else
        {
            int v = SumValue/2;
            for (i = 1; i <=v;i++)
                  f[i]=-INF;
            f[0] = 0;
            for (i = 1; i <= 6; i++)
            {
                 if (c[i] * n[i] >= v)   //該種物品足以塞滿背包-->轉化為完全背包
                 {
                     for (j= c[i]; j<= v; j++)
                         f[j] = max(f[j], f[j-c[i]] + c[i]);
                 }
                 else
                 {
                     int k,cnt=0;
                     for (j=1; j<=n[i]; j*=2)  // 多重背包轉成0/1背包
                     {
                         w[++cnt]=j*c[i];
                         n[i]-=j;
                     }
                     if (n[i]>0)
                     {
                         w[++cnt]=c[i]*n[i];
                     }
                     for (j=1; j<=cnt; j++)
                         for (k=v; k>=w[j]; k--)
                             f[k] = max(f[k], f[k - w[j]] + w[j]);
                 }
            }
            if(f[v] < 0)
                printf("Can't be divided.\n\n");
            else
                   printf("Can be divided.\n\n");
        }
    }
    return 0;
}
View Code

3.POJ1276 Cash Machine(http://poj.org/problem?id=1276

#include <stdio.h>
#include <string.h>
int max(int a,int b)
{
    if (a>b) return a;
    else  return b;
}
int main()
{
    int f[100005],w[1000];
    int c,n;
    while (scanf("%d%d",&c,&n)!=EOF)
    {
        int cnt=0;
        memset(f,0,sizeof(f));
        int i,j;
        for (i=1; i<=n; i++)
        {
            int k,d;
            scanf("%d%d",&k,&d);
            for (j=1; j<=k; j*=2)  // 多重背包轉成0/1背包
            {
                w[++cnt]=j*d;
                k-=j;
            }
            if (k>0)
            {
                w[++cnt]=k*d;
            }
        }
        for (i=1; i<=cnt; i++)      // 01背包的求解
            for(j=c; j>=w[i]; j--)
                f[j]=max(f[j],f[j-w[i]]+w[i]);
        printf("%d\n",f[c]);
    }
    return 0;
}
View Code

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/455387.html

標籤:C

上一篇:網易互聯網筆試(3.27)

下一篇:背包問題(5):混合背包

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more