1. 程式人生 > 其它 >acwing 1022. 寵物小精靈之收服 | 二維01揹包

acwing 1022. 寵物小精靈之收服 | 二維01揹包

目錄

題目傳送門

題目描述

寵物小精靈是一部講述小智和他的搭檔皮卡丘一起冒險的故事。

一天,小智和皮卡丘來到了小精靈狩獵場,裡面有很多珍貴的野生寵物小精靈。

小智也想收服其中的一些小精靈。

然而,野生的小精靈並不那麼容易被收服。

對於每一個野生小精靈而言,小智可能需要使用很多個精靈球才能收服它,而在收服過程中,野生小精靈也會對皮卡丘造成一定的傷害(從而減少皮卡丘的體力)。

當皮卡丘的體力小於等於0時,小智就必須結束狩獵(因為他需要給皮卡丘療傷),而使得皮卡丘體力小於等於0的野生小精靈也不會被小智收服。

當小智的精靈球用完時,狩獵也宣告結束。

我們假設小智遇到野生小精靈時有兩個選擇:收服它,或者離開它。

如果小智選擇了收服,那麼一定會扔出能夠收服該小精靈的精靈球,而皮卡丘也一定會受到相應的傷害;如果選擇離開它,那麼小智不會損失精靈球,皮卡丘也不會損失體力。

小智的目標有兩個:主要目標是收服儘可能多的野生小精靈;如果可以收服的小精靈數量一樣,小智希望皮卡丘受到的傷害越小(剩餘體力越大),因為他們還要繼續冒險。

現在已知小智的精靈球數量和皮卡丘的初始體力,已知每一個小精靈需要的用於收服的精靈球數目和它在被收服過程中會對皮卡丘造成的傷害數目。

請問,小智該如何選擇收服哪些小精靈以達到他的目標呢?

輸入格式

輸入資料的第一行包含三個整數:N,M,K,分別代表小智的精靈球數量、皮卡丘初始的體力值、野生小精靈的數量。

之後的K行,每一行代表一個野生小精靈,包括兩個整數:收服該小精靈需要的精靈球的數量,以及收服過程中對皮卡丘造成的傷害。

輸出格式

輸出為一行,包含兩個整數:C,R,分別表示最多收服C個小精靈,以及收服C個小精靈時皮卡丘的剩餘體力值最多為R。

資料範圍

0<N≤10000<N≤1000,
0<M≤5000<M≤500,
0<K≤1000<K≤100

輸入樣例1:

10 100 5
7 10
2 40
2 50
1 20
4 20

輸出樣例1:

3 30

輸入樣例2:

10 100 5
8 110
12 10
20 10
5 200
1 110

輸出樣例2:

0 100

二維01揹包

分析

\(f[i][j][z]\) 表示前\(i\)個小精靈,使用\(j\)個精靈球,體力耗費\(z\)的時候所能收服的最大精靈數量

  1. 當不選第i個小精靈 或者 精靈球數量/體力 不足以選第i個小精靈的時候:\(f[i][j][z] = f[i-1][j][z]\)

  2. 當可以選第i個小精靈的時候:\(f[i][j][z] = f[i-1][j - v1_i[z-v2_i] + 1\)

狀態轉移方程為:$f[i][j][z] =max(f[i-1][j][z], f[i-1][j - v1_i][z-v2_i] + 1) \quad if(j \ge v1_i \ and \ z \ge v2_i) $

與01揹包類似優化成1維,然後此時能收服最大精靈數:f[n][m-1](因為收服之後體力不能為0)

for(int i = 1; i <= k; i++)
	 	for(int j = n; j >= v1[i]; j--)
		 	for(int z = m-1; z >= v2[i]; z--) 
		 		f[j][z] = max(f[j][z], f[j-v1[i]][z-v2[i]] + 1);

cout << f[n][m-1] << " ";

然後求出能夠收服f[n][m-1]個精靈所耗費的最小的體力rm-r就是最大剩餘體力

int r = m-1;
	while(r > 0 && f[n][r-1] == f[n][m-1]) r--;
cout << m - r;

程式碼

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1010;

int f[N][N];
int n, m, k;
int v1[N], v2[N];

int main()
{
	cin >> n >> m >> k;
	for(int i = 1; i <= k; i++)	cin >> v1[i] >> v2[i];
	/* 
	for(int i = 1; i <= k; i++) // 前i種
	{
		for(int j = 0; j <= n; j++) // 精靈球數量
		{
			for(int z = 0; z <= m-1; z++) // 血量,
			{
				f[i][j][z] = f[i-1][j][z];
				if(j >= v1[i] && z >= v2[i])
					f[i][j][z] = max(f[i][j][z], f[i-1][j-v1[i]][z-v2[i]] + 1)
			}
		 } 
	 } */
	 
	 for(int i = 1; i <= k; i++)
	 	for(int j = n; j >= v1[i]; j--)
		 	for(int z = m-1; z >= v2[i]; z--) 
		 		f[j][z] = max(f[j][z], f[j-v1[i]][z-v2[i]] + 1);
	cout << f[n][m-1] << " ";
	
	int r = m-1;
	while(r > 0 && f[n][r-1] == f[n][m-1]) r--;
	
	cout << m - r;
	
	return 0;
} 

時間複雜度

參考文章

https://www.acwing.com/solution/content/17048/