Problema de mochila - 2. Mochila 01 simples

Problema de mochila 01 simples

#include <iostream>
#define read(x,y) scanf("%d%d",&x,&y)

using namespace std;

const int maxn=1010,maxv=1010;
int v[maxn],w[maxn];  //v体积,w价值
int dp[maxv];

int main()
{
    
    
    int N,V;
    read(N,V);
    for (int i=1;i<=N;i++)  read(v[i],w[i]);
    
    for (int i=1;i<=N;i++)
        for (int j=V;j>=v[i];j--)"更新当前物品在所有背包体积下的最优选择,不能选择的就不更新了,直接继承"
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
            
    printf("%d",dp[V]);
    return 0;
}

Descrição do título

Existem N itens e uma mochila com capacidade para V.Cada item só pode ser usado uma vez.

O volume do i-ésimo item é vi, e o valor é wi.

Decida quais itens colocar na mochila, de modo que o volume total desses itens não exceda a capacidade da mochila e o valor total seja o maior. Produza o valor máximo.

Formato de entrada Os
dois inteiros na primeira linha, N e V, são separados por um espaço, indicando o número de itens e o volume da mochila, respectivamente.

A seguir, existem N linhas, cada uma com dois inteiros vi e wi, separados por espaços, indicando o volume e o valor do i-ésimo item .

Formato de
saída Produz um inteiro, que representa o valor máximo.

Faixa de dados
0 <N, V≤1000
0 <vi, wi≤1000

Amostra de entrada

4 5
1 2 Volume, valor
2 4
3 4
4 5

Saída de amostra:

8

análise:

Para cada objeto, existem dois estados: selecionado ou não selecionado, e deve ser possível não selecionar este item, mas se este item pode ser selecionado depende do tamanho da mochila atual:

  1. Se o volume do objeto for menor ou igual ao volume atual da mochila, existem dois estados: selecionado e não selecionado;
  2. Se o volume do objeto for maior que o volume atual da mochila, a única opção é não selecionar este estado.

Implementação de array bidimensional

#include <iostream>
#define read(x,y) scanf("%d%d",&x,&y)

using namespace std;

const int maxn=1010,maxv=1010; //最多1000个物品,价值最大为1000
int v[maxn],w[maxn]; //记录每个物品的体积v还有价值w,从下标1开始存物品,方便和dp数组对应
int dp[maxn][maxv]; //选择前N个物品,体积最大为V

int main()
{
    
    
    int N,V;
    read(N,V);
    for (int i=1;i<=N;i++)  read(v[i],w[i]);  "从下标1开始存储物品,和dp数组对应"
"开始递推过程,递推方程:dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);"
    for (int i=1;i<=N;i++)  //先循环物品,再循环体积,先行后列。
        for (int j=1;j<=V;j++) {
    
     //从0开始最好,0~V
            dp[i][j]=dp[i-1][j];   "不选第i个物品是必然成立的,如果他不能选择第i个物品就直接继承上一行"
            if (j>=v[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);   
        }          "在可以选择第i个物品的条件下,判断是否要选择第i个物品"
    printf("%d",dp[N][V]);
        
    return 0;
}

dp [i] [j] representa o valor máximo de várias opções sob a premissa dos primeiros i itens e o peso da mochila é j:
se o item pode ser colocado na mochila, a equação de transferência dinâmica é dp[i][j]=max( dp[i-1][j],dp[i-1][ j-v[i] ]+ w[i] ), j> = w [i]
Se o item não puder ser colocado na mochila dp[i][j]=dp[i-1][j] , j <w [i]

Porém, é necessário que cada item opte por não colocá-lo na mochila, então você também pode escolher a escrita no código acima.

Núcleo de recursão: Ao selecionar a solução ideal para os primeiros i itens, você só precisa determinar se seleciona o i-ésimo item com base na solução ótima para os primeiros i-1 itens. Se as mochilas de diferentes capacidades são não é o mesmo para escolher o i-ésimo item.

Resultado final: a tabela bidimensional dp registra o volume máximo da seleção do item em todos os casos, só precisamos do valor máximo final quando os primeiros N itens são selecionados e o volume da mochila é V, ou sejadp[N][V]

Reciclar os itens primeiro, depois o volume? Recicle o volume primeiro e, em seguida, recicle os itens

Observamos esta equação de transferência dinâmica,Na i-ésima linha, apenas os dados da linha anterior são necessários, ou seja, a i-1ª linha; na j-ésima coluna, depende apenas dos dados da j-ésima coluna e de sua coluna esquerda, ou seja, a primeira coluna j.Essa atualização dp[i,j]quando cada um usará apenas a tabela bidimensional dp i-1 da primeira linha, a partir dp[i-1,0]dos dp[i-1][j]dados, que fornece a base para nossa otimização subsequente, e temos que considerar a ordem do problema de travessia :

  1. Percorra de cima para baixo , primeira linha e depois coluna, ou seja, primeiro circule os itens e depois circule o volume. Uma mochila com capacidade V em cada item precisa determinar a melhor escolha e atualizá-la por linha.Cada linha abaixo representa mais um item.
  2. Percorra da esquerda para a direita , primeiro a coluna, depois a linha, ou seja, primeiro o volume é distribuído e, em seguida, os itens são distribuídos. Existem N mochilas diferentes em cada volume. Ao percorrer a primeira linha, selecione o primeiro item e, ao percorrer a segunda linha, selecione os dois primeiros itens. Os dados da primeira linha são obrigatórios , ..., até a primeira linha Para N linhas, os dados da N- 1ª linha são necessários , de modo que a coluna V esteja sempre em loop.

Mas preste atenção ao problema de inicialização nos dois modos de passagem:

  1. Atravessar de cima para baixo, Ao buscar dp[i][j], use apenas todos os dados na i-1ª linha . Para evitar que a matriz cruze o limite, o processamento não pode ser iniciado na 0ª linha. Ele deve começar na 1ª linha, portanto, a 0ª linha deve ser inicializado com 0 primeiro, ou seja, não Escolha o valor máximo de qualquer item em vários tamanhos, que é 0. Em seguida, os itens são enumerados a partir de 1 e o volume da mochila é enumerado a partir de 0.
for (int i=1;i<=N;i++) 
        for (int j=0;j<=V;j++)

O motivo pelo qual o volume da mochila enumerado de 0 a V, quando se trata de evitar v[i]==jo tempo, jv [i] = 0, a situação voltou à coluna 0, coluna 0 deve cada linha precisar ser mantida, de fato, quando Quando j for 0, apenas dp[i][0]=dp[i-1][0], (a menos que haja um item com um volume de 0), então ele dp[i][0]eventualmente será igual dp[0][0]a 0 inicializado na linha 0, então a 0ª coluna pode ser inicializada diretamente para 0 durante a inicialização, então os itens e mochilas O volume pode ser enumerado diretamente a partir de 1, conforme mostrado no código acima:

for (int i=1;i<=N;i++)  
        for (int j=1;j<=V;j++)
  1. Atravesse da esquerda para a direitaBuscando dp[i][j], ele pode usar a j-ésima coluna à esquerda antes da primeira linha de dados i-1, incluindo os dados de linha recém-atualizados dp[i-1][j], a fim de evitar que os limites da matriz (-1) devem conter o volume enumerado desde o início, os itens são enumerado de 1 e deve ser enumerado da esquerda para a direita, de cima para baixo. A próxima linha precisa usar os dados da linha anterior . Portanto, ao inicializar, a 0ª linha e a 0ª coluna devem ser inicializadas com 0.
for (int j=1;j<=V;j++) 
        for (int i=1;i<=N;i++)

Inicialize o significado da linha 0: A linha 0 não representa nada, então não importa o tamanho da mochila, o valor é 0, então dp[0][j]=0;
inicialize o significado da primeira coluna: Não importa o item selecionado, o tamanho da minha mochila é limitado para 0, então você O valor de também deve ser 0. e entaodp[i][0]=0;

Implementação de Rolling Array

Usamos o método de reciclagem dos itens primeiro, e depois o volume da mochila , porque ele usará apenas os dados da linha anterior a cada vez, então definirá um array bidimensional com 2 linhas e colunas em V:int dp[2][V];

Se for para reciclar o volume da mochila primeiro e depois reciclar os itens , após calcular dp [i-1] [j], ao calcular dp [i] [j], ele pode usar a i-1ª linha na primeira j coluna Para os dados, você deve até usar o dp [i-1] [j] que acabou de encontrar, e isso requer mais de duas colunas para manter, portanto, apenas o primeiro método de loop pode ser otimizado com uma matriz contínua.

  1. Você pode definir uma variável p pela operação XOR , se p = 0, então p ^ 1 = 1;
    se p = 1, então p ^ 1 = 0, então você pode alternar repetidamente entre a 0ª linha e a 1ª linha.
  2. Através da operação AND , percorrer o i do item pode representar o número de linhas, e i & 1 pode tirar o bit mais baixo. Quando é um número par, i é 0; quando é um número ímpar, i é 1, para determine se a 0ª linha ou a 1ª linha é.
#include <iostream>
#define read(x,y) scanf("%d%d",&x,&y)

using namespace std;

const int maxn=1010,maxv=1010;
int v[maxn],w[maxn];  //v体积,w价值
int dp[2][maxv];

int main()
{
    
    
    int N,V;
    read(N,V);
    for (int i=1;i<=N;i++)  read(v[i],w[i]);
    //dp递推,预处理:0行0列初始化为0,体积可以从1处理
    int p=0;   "每次循环结束时,p指向处理完后的那一行,上次处理的是第0行。"
    for (int i=1;i<=N;i++) {
    
    
        for (int j=1;j<=V;j++) {
    
      //p^1表示当前处理的行
            if (j>=v[i]) dp[p^1][j]=max(dp[p][j],dp[p][j-v[i]]+w[i]);   //可以选择物品i,在是否选择之间抉择
            else  dp[p^1][j]=dp[p][j];     //不能选择物品[i],直接继承上一行
        }
        p^=1; "p=p^1; 在在第0行和第1行之间来回切换"
    }
    printf("%d",dp[p][V]);
    
    return 0;
}

Implementação de array unidimensional

A otimização da matriz contínua é a natureza de usar apenas os dados da i-1ª linha por meio do cálculo da i-ésima linha, então sejamos mais específicos: ao
atualizar dp[i,j], apenas a i-1ª linha no dp dois- tabela dimensional é utilizada de cada vez. a partir dp[i-1,0]de dp[i-1][j]dados.

Portanto, pode ser otimizado como um array unidimensional, podendo-se saber que quanto menor o volume j da mochila, mais vezes ela será utilizada, e será utilizada entre os volumes j e V da mochila na próxima camada. Portanto, o segundo nível do loop for está na ordem inversa . Ao buscar dp [i] [j], comece com j = V primeiro, porque dp [i-1] [V] não será excluído de dp [i] [V] Dp [i] [j] é usado, dp [i-1] [V] é inútil após esta atualização, então dp [i] [V] pode substituir dp [i-1] [V], e então atualização em ordem decrescente.

A condição para determinar o final é se a capacidade da mochila j é maior que o volume v [i] do item a ser carregado na mochila, pois quando j é menor que v [i], é diretamente conhecido do anterior que dp [i] [j] = dp [i -1] [j], herda os dados da linha anterior, porque usamos um array unidimensional, ele pode ser herdado diretamente.

#include <iostream>
#define read(x,y) scanf("%d%d",&x,&y)

using namespace std;

const int maxn=1010,maxv=1010;
int v[maxn],w[maxn];  //v体积,w价值
int dp[maxv];

int main()
{
    
    
    int N,V;
    read(N,V);
    for (int i=1;i<=N;i++)  read(v[i],w[i]);
    
    for (int i=1;i<=N;i++)
        for (int j=V;j>=v[i];j--)"更新当前物品在所有背包体积下的最优选择,不能选择的就不更新了,直接继承"
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);  //进入for循环的都是可以选择物品i的情况
            
    printf("%d",dp[V]);
    return 0;
}

Acho que você gosta

Origin blog.csdn.net/HangHug_L/article/details/114228223
Recomendado
Clasificación