• 【u012】数字游戏


    Time Limit: 1 second

    Memory Limit: 128 MB

    【问题描述】

     小W发明了一个游戏,他在黑板上写出了一行数字a1,a2,…an,然后给你m个回合的机会,每回合你可以从中选择一个数擦去它,接着剩下来的每个数字ai都要递减一个值bi如此重复m个回合,所有你擦去的数字之和就是你所得到的分数。 小W和他的好朋友小Y玩了这个游戏,可是他发现,对于每个给出的an和bn序列,小Y的得分总是比他高。小W很不服气,想让你帮他算算,对于每个an和bn序列,可以得到的最大得分是多少。这样他就知道有没有可能超过小Y的得分。

    【输入格式】

    输入文件game.in的第1行,一个整数n(1≤n≤200),表示数字的个数。 第2行,一个整数m(1≤m≤n),表示回合数。 接下来一行有n个不超过10000的正整数,a1,a2,…an,表示原始数字 最后一行有n个不超过500的正整数,b1,b2,…bn,表示每回合每个数字递减的值

    【输出格式】

    一个整数,表示最大可能的得分。结果输出到文件game.out。

    Sample Input1

    3
    3
    10 20 30
    4 5 6
    

    Sample Output1

    47

    【题解】

    题意是这样的。无论你选什么数字。其他数字都会减去它相应的bi值,而不是说你选了数字k,然后其他数字减去bk。

    这里我们要注意。可能我们擦掉的数字是一样的比如 1,2,3.

    但是擦掉的顺序不同。结果也是不同的。

    那应该如何选择顺序?

    可以想象我们选择了a[i]和a[j],然后b[i] < b[j]。

    那么我们要先选哪一个?

    肯定是先选a[j]。因为我们可以避免a[j]在先选完a[i]之后减去较大的b[j];

    可以写出这样ans=a[i]+a[j]-b[x].这个x就是后选的那一个的下标。

    显然我们要让b[x]最小。那就先选a[j]后选a[i]。这样我们减去的就是较小的b[i]了。

    知道了选择的顺序。接下来就要解决选哪些的问题了。

    虽然优先选择b[x]较大的。

    但是如果有这样的

    a[] 10     100000

    b[]  1000    2

    然后m=1.也就是说我们只能选一个。那我们肯定要选择a[2]了。

    又或者

    a[]  2000 200 10000

    b[] 100 99  90 

    m=2时。我们也不能单纯地选择a[1],a[2]。而应该选择a[1],a[3]。

    综上。我们得出这样一个算法。

    先把a[]按照b[]的大小降序排序。

    然后,设f[i][j]表示前i个数,擦掉了j个数字得到的最大值。

    f[i][j] = max{f[i-1][j],f[i-1][j-1]+a[i]-(j-1)*b[i]);

    这里之所以要减去(j-1)*b[i]。是因为当前选的a[i]是第j个擦掉的数字。也就是说之前的j-1

    回合。它都没有被擦掉,所以要扣掉(j-1)个b[i];

    然后这个动态规划一定要在排完序之后做。

    比如样例

    3
    3
    10 20 30
    4 5 6

    如果我们不排序。

    得到的f[1][1] = 6;

    f[2][2] = f[1][1]+20-5*1 = 21;

    f[3][3] = f[2][2] + 30-6*2 = 39;

    而答案是47;

    这里我们选择的也是1,2,3但是如果按照上面的方程。

    我们是没有考虑到选择的顺序的。

    也就是说到了a[3]这个决策的时候。

    我们只会考虑以下3种顺序

    a[1]->a[3];

    a[1]->a[2]->a[3];

    a[3];

    因此。选择的顺序就显得尤为重要了。

    这也是要排序的原因所在。

    然后通过这个方程

    f[i][j] = max{f[i-1][j],f[i-1][j-1]+a[i]-(j-1)*b[i]);

    我们也可以看到。如果较小的b[i]放在比较靠后。减掉的会比较少。那相应的值也就更大了。

    加深理解:

    假如我们不将数组排序。

    我们用dp选择出来的是一段序列c1,c2,c3,c4;

    它是在不排序的基础上所能得到的最大值。

    然后我们刚才讨论过。如果a[i],a[j]都选了,而b[i]<b[j]。

    那我们一定是先选择a[j]的。所以我们可能要把这段序列排一下序。

    把b[]大的放在前面。比如有序的序列可能变成c3,c2,c1,c4什么的。。

    然后既然知道不管选什么都要按照b[]排序。那我们就在进行dp之前先排一下序。

    这样才能保证其为最优的。(上面说的先dp后排序得出的不一定是最优值!我只是为了能让大家更好理解才说的);

    【代码】

    #include <cstdio>
    
    int n, m, a[201], b[201], f[201][201] = { 0 };
    
    int min(int a, int b) //返回a和b中的较小值
    {
    	return a < b ? a : b;
    }
    
    int max(int a, int b) //返回a和b中的较大值
    {
    	return a > b ? a : b;
    }
    
    int main()
    {
    	scanf("%d", &n);
    	scanf("%d", &m);
    	for (int i = 1; i <= n; i++)
    		scanf("%d", &a[i]);
    	for (int i = 1; i <= n; i++)
    		scanf("%d", &b[i]);
    	bool flag = false;
    	while (!flag) //按照b由大到小排序。冒泡排序
    	{
    		flag = true;
    		for (int i = 1;i <= n-1;i++)
    			if (b[i] < b[i + 1])
    			{
    				flag = false;
    				int t = a[i]; a[i] = a[i + 1]; a[i + 1] = t;//换的时候连同a要一起交换
    				t = b[i]; b[i] = b[i + 1]; b[i + 1] = t;
    			}
    	}
    	for (int i = 1; i <= n; i++)//进行dp
    	{
    		int maxj = min(i, m);//f[i][j]表示的是前i个数擦掉j个数的最大值
    		for (int j = 1; j <= maxj; j++) //j则不能超过i.但要到达m;
    			f[i][j] = max(f[i - 1][j], f[i - 1][j - 1] + a[i] - b[i] * (j - 1));
    	}
    	printf("%d
    ", f[n][m]);//输出答案。
    	return 0;
    }




    
       
    
    
  • 相关阅读:
    [服务器]Windows Server 2008 64位1核1G安装SQL Server2008
    [工作]离职了!好好休息一下
    [工作]IT连和IT恋产品已完成第一版,准备上线运营
    [SQL Server]储存过程中使用临时表循环操作数据
    [Swift]Xcode格式化代码快捷键
    [Swift]使用Alamofire传递参数时报错
    [工作]记录一下目前的工作
    [Swift]Swift图片显示方式设置,控件UIImageView的contentMode属性设置
    [Swift]创建桥接文件,Swift使用MJRefresh刷新插件
    我遇到了改变的机会
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632295.html
Copyright © 2020-2023  润新知