-
2 2 2 2 2 1 3 1 2 2 2 2 2 1 2 1 1
样例输出
-
8 No Answer
描述
在遥远的未来,小Hi成为了地球联邦外空间联合开发工作组的一员,前往一颗新发现的星球开发当地的重金属资源。
为了能够在当地生存下来,小Hi首先要建立一个基地。建立基地的材料可以直接使用当地的石材和富裕的重金属资源。基地建设分为N级,每一级都需要达成K的建设值后才能够完成建设,当前级别的建设值溢出后不会影响到下一级的建设。
小Hi可以产出的重金属资源按照精炼程度分为M级,根据开采的数量和精炼的工艺,可以将获取精炼程度为第i级的重金属资源的成本量化为Ai。
在建设第1级基地时,一块精炼度为i的重金属可以提供Bi的建设值,此后基地的级别每提高一级,建设值将除以T并下取整(整除)。
现给定N、M、K、T、A[]和B[],小Hi需要你帮助他计算他完成基地建设的最小成本。
输入
输入包含多组测试数据。
输入的第一行为一个整数Q,表示测试数据的组数。
每组测试数据的第一行为4个整数N、M、K和T,意义如前文所述。
接下来的一行为M个整数,分别表示A1~AM。
接下来的一行为M个整数,分别表示B1~BM。
对于100%的数据,满足1<=N<=10,1<=M<=100,1<=K,T<=104
对于100%的数据,满足Ai和Bi均为32位整型范围内的正整数
对于100%的数据,满足1<=Q<=10
输出
对于每组测试数据,如果小Hi最终能够完成基地建设,则输出小Hi完成基地建设所需要的最小成本,否则输出“No Answer”。
看了下这道题通过率挺高,以为挺简单的......没想到是考查dp,我dp很烂,简单的还能应对,要自己推转移方程就完全懵逼了。唉,主要还是太菜了吧。
题意读起来有些绕口,涉及变量比较多,多三四遍就理解什么意思了。我第一感觉以为是考“贪心”,先计算M个“单位建设值所需的成本”,然后排序,选一个最小的不为0的建设值矿石,累加他们的和。什么情况下会No Answer?如果M种矿石的建设值都为0的时候,就不能建设成功了。为什么会出现建设值为0呢?因为每建设一层基地需要将M个矿石建设值每个除以T(向下取整),所以就可能出现0了。
这是贪心的想法,然而我提交后是WA...以为是数据范围不够,就把int 换成了long long,然后提交还是WA。
无奈之下看别人题解,居然搜出来大家都是用的完全背包。没办法,硬着头皮看吧,学不会dp总得要学呀!说不定哪天就学会了!看了好几个别人的题解,但...都没怎么看懂,所以又去看完全背包了。
折腾了两个多小时,总算有点明白了...我感觉这道题形式上有点向完全背包,但是需求上不向完全背包,(完全背包是求最大价值,这道题是求最小成本)所以状态转移方程也不太一样吧...
我的代码注释中有自己的理解(也不一定对吧,反正我是这样理解的)还有我参考的一些链接,也贴上。
http://blog.csdn.net/jxust_tj/article/details/50816771
背包问题:
http://www.cppblog.com/tanky-woo/archive/2010/07/31/121803.html
http://www.cnblogs.com/daoluanxiaozi/archive/2012/05/06/2486105.html
#include <cstdio>
#include <string.h>
#include <algorithm>
#define MAX 10000 + 10
#define INF 0x3fffffff
using namespace std;
int A[MAX];
int B[MAX];
long long dp[MAX];
void initData() {
memset( A, 0, sizeof( A ) );
memset( B, 0, sizeof( B ) );
}
void downVal( int m, long long t ) {
for( int i = 1; i <= m; i++ ) {
B[i] = B[i] / t;
}
}
int main() {
int q;
int n, m, k, t;
scanf( "%d", &q );
while( q-- ) {
initData();
scanf( "%d%d%d%d", &n, &m, &k, &t );
for( int i = 1; i <= m; i++ ) {
scanf( "%d", &A[i] );
}
for( int i = 1; i <= m; i++ ) {
scanf( "%d", &B[i] );
}
int level = 1;
long long sum = 0;
bool flag = true;
while( level <= n ) {
if( level > 1 ) {
downVal( m, t );
}
// dp初始化
for( int i = 0; i < MAX; i++ ) {
dp[i] = INF;
}
dp[0] = 0;
for( int i = 1; i <= m; i++ ) {
for( int j = 0; j <= k; j++ ) {
// 当前建设值是j,dp[j]代表当前建设值为j的最小成本
// j + B[i]是未来的建设值,dp[j + B[i]]是未来建设值为 ( j + B[i] ) 时的最小成本
// 如果未来的建设值大于k,就是题目中说的溢出,那么就把建设值按k计算
// 遇到i号矿石,如果选择i号矿石,成本是dp[j] + A[i];
// 如果不选i号矿石,成本是dp[k]
// 从dp[k]和dp[j] + A[i] 两者选一个小的,代表k时的最小成本
if( j + B[i] > k ) dp[k] = min( dp[k], dp[j] + A[i] );
// 没有溢出,就正常计算
// 遇到i号矿石,如果选择i号矿石,成本是dp[j] + A[i];
// 如果不选i号矿石,成本是dp[j + B[i]]
// 从两者选一个小的,代表未来建设值为 ( j + B[i] ) 时的最小成本
else dp[j + B[i]] = min( dp[j + B[i]], dp[j] + A[i] );
}
}
// 如果建设值为k时的最小成本没有被计算出来,说明没有方案能建设成功
if( dp[k] == INF ) {
flag = false;
break;
}
sum = sum + dp[k];
level++;
}
if( flag ) {
printf( "%lld\n", sum );
}
else {
printf( "No Answer\n" );
}
}
return 0;
}