• 【9921】暗黑破坏神


    Time Limit: 1 second
    Memory Limit: 128 MB

    【问题描述】

    游戏的主人公有 n 个魔法每个魔法分为若干个等级,第 i 个魔法有 p[i] 个等级 ( 不包括 0) 每个魔法的每个等级都有一个效果
    值,一个 j 级的 i 种魔法的效果值为 w[i][j] 魔法升一级需要一本相应的魔法书 购买魔法书需要金币,第 i 个魔法的魔
    法书价格为 c[i] 而小 x 只有 m 个金币 ( 好孩子不用修改器 ) 你的任务就是帮助小 x 决定如何购买魔法书才能使所有魔
    法的效果值之和最大 开始时所有魔法为 0 级 效果值为 0。

    【输入格式】

    第一行 用空格隔开的两个整数 n,m,
    以下 n 行 描述 n 个魔法,
    第 i+1 行描述 第 i 个魔法。
    格式如下
    c[i] p[i] w[i][1] w[i][2] ... w[i][p[i]]
    0<n≤100,0<m≤500,0<p[i]≤50,0<c[i]≤10

    【输出格式】

    第一行输出一个整数,即最大效果值。
    以后 n 行输出你的方案:
    第 i+1 行有一个整数 v[i] 表示你决定把第 i 个魔法学到 v[i] 级
    如果有多解 输出花费金币最少的一组
    如果还多解 输出任意一组

    Sample Input

    3 10  
    
    1 3 1 2 2  
    
    2 3 2 4 6  
    
    3 3 2 1 10  
    
    
    
    

    Sample Output

    11
    1
    0
    3
    
    【题解】
    说下。学到了3即,就只能得到a[i][3]那个对应的效果值。a[i][1],a[i][2]并没有累加到a[i][3]。也就是说如果a[i][1] = 1,a[i][2] =3,a[i][3] = 5,学到3级,就只有5点效果值。而不是8点。
    然后这就是一个多重背包,然后记录方案数,输出方案的问题了。难点在输出方案。
    如果用一维数组f来做这个问题,当然是可做的了。但是方案会很难记录。
    所以我用了二维的数组。f[i][j]
    表示前i种魔法,金币使用不超过j所能获得的最大效果值。
    f[i][j] = max(f[i-1][j],f[i-1][j-k*w[i]]+c[k]);
    然后我们在做的时候,用一个what数组,记录下我们的决策是什么。
    如果我们更新了一个解,就要更新相应的what[i][j];
    其中what是一个三维数组what[i][j][0]和what[i][j][1],。。。。好像第一维没用。
    what[i][j][0]是选了哪一个魔法(肯定就是i啦。所以说第一维没用),然后what[i][j][1]表示i魔法学到了几级。。 //后记:第一维如果不学就为-1,所以可以起判断作用,所以还是有用的。
    输出f[n][m];
    最后用while循环就能输出方案了。知道学了几级,就记录学了几级就可以了。最后输出。
    还有金币较小问题。
    如果f[n][m] == f[n][m-1]则递减m。最后输出f[n][m]
    还记得f[i][j]的含义吗?
    表示前i种魔法,金币使用不超过j所能获得的最大效果值。
    这样应该能理解了吧?
    【代码】
    #include <cstdio>
    #include <cstring>
    
    int n,m,w[101],num[101],c[101][51],f[101][501],what[101][501][2],le[101] = {0};
    
    void input_data()
    {
    	memset(c,0,sizeof(c));
    	scanf("%d%d",&n,&m);  
    	for (int i = 1;i <= n;i++) // 先输入花费,然后是 能学到等级以及学到几级能获得多少效果 
    		{
    			scanf("%d%d",&w[i],&num[i]);
    			for (int j = 1;j <= num[i];j++)
    				scanf("%d",&c[i][j]);
    		}			
    }
    
    void get_ans()
    {
    	memset(f,0,sizeof(f)); //先初始化f数组 
    	for (int i = 1;i <= n;i++)
    		{
    			for (int j = m;j >=0;j--) //倒序进行,因为是多重背包。和0/1背包类似 
    				{
    					f[i][j] = f[i-1][j]; //no learn
    					what[i][j][0] = -1; //因为不学,所以记录学的魔法为-1,诶好像一维也有用啊 
    					for (int k = 1;k <= num[i];k++) //枚举学到了几级。 
    						{
    							if (k*w[i] > j) break; //如果超过了容量则跳过 
    							if (f[i][j] < f[i-1][j-k*w[i]] + c[i][k]) //如果能更新解 则更新 
    								{
    									f[i][j] = f[i-1][j-k*w[i]] + c[i][k];
    									what[i][j][0] = i; //因为更新了解,所以相应的方案信息也要记录 
    									what[i][j][1] = k;
    								}
    						}
    				}
    		}
    }
    
    void output_ans()
    {
    	int mm = m;
    	while (f[n][mm] == f[n][mm-1]) mm--; //这是最大效果相同然后金币要尽量少的实现方式 
    	printf("%d
    ",f[n][mm]); //先输出最大效果 
    	int tt = what[n][mm][0],kk = what[n][mm][1],i = n;
    	while (tt!=0) //如果金币还有剩余,则还有魔法信息没有扫描到 
    		{
    			if (tt!=-1) //如果不是跳过这个魔法不学 
    				{
    					le[tt]+=kk;	//这个魔法等级增加 
    					int needjian = kk*w[i]; //这是i魔法花费的金币,这样可以找到上一个魔法的信息 
    					mm -= needjian; //跳到上一层的决策 
    				}
    			i--; // 跳回上一个决策 
    			tt = what[i][mm][0];  
    			kk = what[i][mm][1];
    		}
    	for (int i = 1;i <= n;i++)
    		printf("%d
    ",le[i]); //输出每个魔法的等级即可。 
    }
    
    int main()
    {
    	//freopen("F:\rush.txt","r",stdin);
    	input_data();
    	get_ans();
    	output_ans();
    	return 0;
    }


  • 相关阅读:
    Quick Sort 快速排序的原理及实现
    IAR_FOR_STM8开发之DEMO的建立
    跨域或者Internet访问Remoting[Remoting FAQ]
    2020 7 22 每日总结
    2020 7 23 每日总结
    2020 7 27 每日总结
    2020 7 20 每日总结
    2020 7 29 每日总结
    2020 7 21 每日总结
    2020 7 28 每日总结
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632379.html
Copyright © 2020-2023  润新知