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:
- Se o volume do objeto for menor ou igual ao volume atual da mochila, existem dois estados: selecionado e não selecionado;
- 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 :
- 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.
- 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:
- 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]==j
o 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++)
- 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-atualizadosdp[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.
- 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.- 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;
}