1. 程式人生 > >【bzoj5018】[Snoi2017]英雄聯盟 背包dp

【bzoj5018】[Snoi2017]英雄聯盟 背包dp

高精 long long ros 滿足 cnblogs 判斷 using sin ont

題目描述

正在上大學的小皮球熱愛英雄聯盟這款遊戲,而且打的很菜,被網友們戲稱為「小學生」。現在,小皮球終於受不了網友們的嘲諷,決定變強了,他變強的方法就是:買皮膚!小皮球只會玩N個英雄,因此,他也只準備給這N個英雄買皮膚,並且決定,以後只玩有皮膚的英雄。這N個英雄中,第i個英雄有Ki款皮膚,價格是每款CiQ幣(同一個英雄的皮膚價格相同)。為了讓自己看起來高大上一些,小皮球決定給同學們展示一下自己的皮膚,展示的思路是這樣的:對於有皮膚的每一個英雄,隨便選一個皮膚給同學看。比如,小皮球共有5個英雄,這5個英雄分別有0,0,3,2,4款皮膚,那麽,小皮球就有3*2×4=24種展示的策略。現在,小皮球希望自己的展示策略能夠至少達到M種,請問,小皮球至少要花多少錢呢?

輸入

第一行,兩個整數N,M 第二行,N個整數,表示每個英雄的皮膚數量Ki 第三行,N個整數,表示每個英雄皮膚的價格Ci 共 10 組數據,第i組數據滿足:N≤max(5,(log2i)^4) M≤10^17,1≤Ki≤10,1≤Ci≤199。保證有解

輸出

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

樣例輸入

3 24
4 4 4
2 2 2

樣例輸出

18


題解

背包dp

考慮到方案數過多,無法作為狀態;而總錢數較少,所以可以以此作為狀態。

故設$f[i][j]$表示購買前$i$種皮膚,花費$j$元能夠得到的最大方案數。那麽可以直接枚舉每個皮膚購買的數量然後轉移 。

一個小trick:由於題目只要求判斷是否達到m,因此當dp值大於m時直接將其賦為m(因為方案數是單調的,只要達到了m,以後的都會達到),避免高精度。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
long long f[125][250010] , p;
int v[125] , c[125];
int main()
{
	int n , i , j , k , m = 0;
	scanf("%d%lld" , &n , &p);
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &v[i]);
	f[0][0] = 1;
	for(i = 1 ; i <= n ; i ++ )
	{
		scanf("%d" , &c[i]);
		for(j = 0 ; j <= m ; j ++ ) f[i][j] = f[i - 1][j];
		for(j = 2 ; j <= v[i] ; j ++ )
			for(k = c[i] * j ; k <= m + c[i] * j ; k ++ )
				f[i][k] = min(p , max(f[i][k] , f[i - 1][k - c[i] * j] * j));
		m += c[i] * v[i];
	}
	for(i = 0 ; i <= m ; i ++ )
	{
		if(f[n][i] >= p)
		{
			printf("%d\n" , i);
			return 0;
		}
	}
	return 0;
}

【bzoj5018】[Snoi2017]英雄聯盟 背包dp