【题目链接】
ybt 1321:【例6.3】删数问题(Noip1994)
洛谷 P1106 删数问题
【题目考点】
1. 贪心
【解题思路】
解法1:每次找k+1个数中的最小值
假设我们从左向右扫描每位数字,分别判断该位数字是否应该被删掉。此时各位数字为:
d
1
d
2
.
.
.
d
n
d_1d_2...d_n
d1d2...dn,要在其中删掉
k
k
k位数字。
删掉k位后剩下
n
−
k
n-k
n−k位数字,最高位数字为
d
x
d_x
dx
如果不删
d
1
d_1
d1,最高位为
d
1
d_1
d1;如果删除
d
1
d_1
d1不删
d
2
d_2
d2,最高位为
d
2
d_2
d2;。。。
d
1
∼
d
k
+
1
d_1\sim d_{k+1}
d1∼dk+1都可能成为最终数字是最高位。为了让结果最小,最高位应该是这些数字中的最小值。
假设找到第一个最小值是
d
m
d_m
dm,则删掉
d
1
∼
d
m
−
1
d_1\sim d_{m-1}
d1∼dm−1,删掉了
m
−
1
m-1
m−1个数字,下面还要删掉
k
−
m
+
1
k-m+1
k−m+1个数字。接下来就是子问题:在
d
m
+
1
∼
d
n
d_{m+1}\sim d_n
dm+1∼dn中删掉
k
−
m
+
1
k-m+1
k−m+1个数字。
(如果找的不是第一个最小值,那么删掉的数字中也许有与最小值相等的数字可以作为下一位)
该算法最大复杂度:
O
(
n
k
)
O(nk)
O(nk)
解法2:删掉比后面数字大的数字
考察两个连续数位的数字
d
1
d
2
d_1d_2
d1d2,已知
d
1
>
d
2
d_1> d_2
d1>d2
方案一:删掉
d
1
d_1
d1保留
d
2
d_2
d2
方案二:保留
d
1
d_1
d1
两种方案下,最后
d
1
d_1
d1与
d
2
d_2
d2会在同一个数位上。很明显应该让更小的
d
2
d_2
d2在这一数位上。
不断用当前数位和下一位比较,如果当前数位大于下一位,那么删除当前位。当前位置指向前一个位置。最后如果剩余几位没删完,则从末尾开始删除这些数位。
该算法最大复杂度:
O
(
n
)
O(n)
O(n)
【注意】:去除前导0
【题解代码】
解法1:每次找k+1个数中的最小值
#include<bits/stdc++.h>
using namespace std;
#define N 300
int main()
{
int k, an = 0;
char s[N], a[N];
cin >> s >> k;
int len = strlen(s), i;
for(i = 0; i < len && s[i] == '0'; ++i);//去除前导0
while(i < len)
{
if(k == len-i)//如果剩下的元素个数只有k个,那么都删掉
break;
int mi = i;//求i~i+k的最小值下标
for(int j = i; j <= i+k; ++j)
{
if(s[j] < s[mi])
mi = j;
}
a[++an] = s[mi];
k -= mi - i;//s[i]~s[mi-1]都删掉,删掉了mi-i个元素
i = mi + 1;
}
for(i = 1; i <= an && a[i] == '0'; ++i);//去除前导0
if(i > an)//如果前导0去完,就没有可以输出的了,说明原来的结果是0
cout << "0";
else
while(i <= an)
cout << a[i++];
return 0;
}
解法2:删掉比后面数字大的数字
#include<bits/stdc++.h>
using namespace std;
#define N 300
int main()
{
int k, an = 0;
char s[N], a[N];
cin >> s >> k;
int len = strlen(s), i, ct = 0;
for(i = 0; i < len && s[i] == '0'; ++i);//去除前导0
for(; i < len; ++i)
{
while(an > 0 && a[an] > s[i] && ct < k)//a[an]为前一个数 s[i]为后一个数,当a[an]>s[i]时,删除a[an]
{
an--;
ct++;//删除数字个数加1
}
a[++an] = s[i];//无论如何s[i]都会填充进数组a
}
while(ct < k)//删除末尾的k-ct个元素
{
an--;
ct++;
}
for(i = 1; i <= an && a[i] == '0'; ++i);//去除前导0
if(i > an)//如果前导0去完,就没有可以输出的了,说明原来的结果是0
cout << "0";
else
while(i <= an)
cout << a[i++];
return 0;
}