算法第四章实验报告
一、题目描述
给定n位正整数a,去掉其中任意k≤n 个数字后,剩下的数字按原次序排列组成一个新的正整数。对于给定的n位正整数a和正整数 k,设计一个算法找出剩下数字组成的新数最小的删数方案。如果数字最前面有0不输出。
输入格式:
第 1 行是1 个正整数 a。第 2 行是正整数k。
输出格式:
输出最小数。
输入样例:
在这里给出一组输入。例如:
178543
4
5001
1
123456
2
109
1
输出样例:
在这里给出相应的输出。例如:
13
1
1234
9
二、算法
1、分析题目
(1)删除一个数,使得剩下的数的顺序不变且能得到最小的值,第一个想法是想到删掉最大那个数,但这样的贪心算法是错误的,因为1009删掉一个数如果删最大的数得到的值是100,而答案是9,故此类贪心算法是错误的;
(2)我们可以细细分析题目,可以发现我们应该删掉高位的且数值大的,因为我们都知道高位数值相对低位对于整个数的大小的影响会更大,所以这样的贪心算法才能得到最优解
(3)如:1783429,在此类情况中8的位数比9大同时8的数值比7大,故删掉一个数应删8,得到173429比183429和178342都要小;
(4)从上面的例子我们可以看出只要我们遇到一个数,从左往右,只要它比它的下一位还要大,就删掉这个数,就能得到最小的数,而一直递增的情况,只要删掉最后一位,就能得到最小的数
(5)上面说明了删除1个数的情况,那k个数既是进行了k轮删除1个数的情况,每次都使它最小,那最后的结果就是最小的
2、代码
#include <iostream> using namespace std; int main() { string a; int k; cin >> a >> k; int len = a.length(); //进行k轮删除 for(int i = 0;i < k;i++) { for(int j = 0;j < len-1;j++) { //只要此数比它的下一位大,即把它删掉 if(a[j] > a[j+1]) { for(int k = j;k < len;k++) { a[k] = a[k+1]; } break; } } len--; } int i= 0; for(;i < len;i++) { if(a[i]!='0') { break; } } if(i == len) { cout << "0"<<endl; } for(;i < len;i++) { cout << a[i]; } return 0; }
3、时间复杂度分析
每一次删除都会遍历所有的数(这里的所有是指len长度,len长度没删一次就会减小1),
故该删除算法的时间复杂度是O(k*n)
三、对贪心算法的理解
(1)贪心算法是只看到眼前得局部最优解,贪婪地运用此种最优解进行计算从而得到全局最优解,故贪心算法是一个非常只使用于全局最优解依赖于局部最优解得问题
(2)由于贪心算法是根据局部最优解推导全局最优解,故得有一个好的算法,此算法能得到局部最优解,而这对于我个人而言是最难的,什么样的贪心算法得到的解才是最优解呢?对于这道题来说,我一开始就使用了错误的贪心算法,即每次都把最大的数删掉,后来是看了别人的解说才知道正确的贪心算法是什么样的,所以我个人认为要得到正确的贪心算法只能多刷题,见得多了,总结多了,自然而然就能知道哪个是正确的贪心算法;如果实在想不出好的贪心算法,我觉得使用动态规划也不失为一种好的方法