HDU - 3183 A Magic Lamp(RMQ-ST)贪心

Kiki likes traveling. One day she finds a magic lamp, unfortunately the genie in the lamp is not so kind. Kiki must answer a question, and then the genie will realize one of her dreams.
The question is: give you an integer, you are allowed to delete exactly m digits. The left digits will form a new integer. You should make it minimum.
You are not allowed to change the order of the digits. Now can you help Kiki to realize her dream?
Input
There are several test cases.
Each test case will contain an integer you are given (which may at most contains 1000 digits.) and the integer m (if the integer contains n digits, m will not bigger then n). The given integer will not contain leading zero.
Output
For each case, output the minimum result you can get in one line.
If the result contains leading zero, ignore it.
Sample Input
178543 4
1000001 1
100001 2
12345 2
54321 2
Sample Output
13
1
0
123
321

题意:给出一个最大长度为1000数位的数,删除这个数中任意n个数位,使得剩余的数位组成的数值最小。若删除后又前导零则不输出前导0。

本来这题是个RMQ的题,但是并没想到RMQ的思路。直接贪心也是可以过的。
首先我们对于一个数,删除的肯定是较大的数值,而从数位的角度来说,如果一个数位上数值很大,但是他所在的数位很低,那么删掉它没有什么影响。而我们要保持的是数位高的数值尽量低,因此从高位起到低位,尽量维护一个非严格递增的序列,比如12321这个数中,删去1个元素,那么肯定是删除3是最优的,因为123保持了升序,而321是降序,那么我们维护升序即删除了第一个出现降序的数3。
我们一直对数位进行这样的处理,比如删除12321中的3后,重新对1221进行遍历,发现最后两位21不符合升序的要求,那么删除一个2,即121,也是最小值。因此删除的位数n即对数值的遍历次数,每次做的操作都是维护一个较长的递增序排列。

具体实现上。我们要删除n个数位,即循环遍历n次,每次从头开始i=0开始遍历,对于删除的数,我们用一个vis进行标记,那么在遍历过程中,是升序状态数位,以及被删过的数位可以直接跳过,对于一个未被删除的数,我们检查他的下一位是否是大于等于自己的。如小于自己,维护递增顺序,删除该数。标记为true。
然后,因为该数已经被删除,我们每次拿一个数对比的是他的后一个数,那么他的后一个数如果已经被删除,如何处理。我们可以这样,一个数被删除后,那么这个数位上的值已经没用了,我就顺便将其修改成此位置后下一个未被删除的数,这样之前的数在检查自的下一位时,可以越过被删除的数,直接对比到下一个未被删除的数。因此。我们做的操作其实是将有用的信息向左边传递过去。用于这之前的数值进行升序判断。

对于最后一位数,也就是最低位,我们如何处理,我们直接对比其下一位\0即可,即len位。如17777这个数,最后一位7之后是字符串结尾\0,那么就出现了升序断裂。即把最后一个7修改成了\0,也就是说,删除几位,就从最后一位开始往前删。是正确的删法。

有一点要注意的是,对于一些数据,我们删除并且修改其值后,其值左边的数值又被后面的数修改,那么就造成了,一个更小的后续数字不能被传递回最左边,我们想要实现的应该是在一整段被删除的数位区间内,都被修改成了该区间相邻的一个未被删除的数值。但是这样就导致了传递的断节。如56653536,删除5位后应该是335,但是整体传递修改后却变成了533,这是因为传递过程中,先更新了最后的5 ,再更新的3,而这个3无法对最高位5进行更新,因为这段连续的被删除区间内出现了不一样的断节,即5无法判断离自己最近的 未被删除的数3,而只能判断到邻近的5,最后这个5永远不会被删除,因为断节点已经被标记,那个位置没有被更新就传递不回来。

解决方法是,我们每次更新一个被删除的值成后一个未被删除的值时,不能只更新当前一个被删除的,还应回头把之前连续被删除的区间统统修改成后一个未被删除的值,这里用一个while实现。
最后处理一下前导零的输出即可。

#include<bits/stdc++.h>
using namespace std;
char a[1008];
bool vis[1008];
int main()
{
    int n;
    while(scanf("%s",&a)!=EOF)
    {
        memset(vis,false,sizeof vis);
        scanf("%d",&n);
        int len=strlen(a);
        for(int i=0;i<n;i++)
        {
            int j=0;
            while((vis[j]==true||a[j]<=a[j+1])&&j<len)j++;
            vis[j]=true;
            int tmp=j;
            while(vis[tmp]==true)a[tmp--]=a[j+1];
        }
        bool flag=false;
//        for(int i=0;i<len;i++)printf("%c",a[i]);
//        printf("\n");
        for(int i=0;i<len;i++)
        {
            if(!vis[i])
            {
                if(a[i]=='0'&&!flag) continue;
                else flag=true;
                if(flag)printf("%c",a[i]);
            }
        }
        if(!flag)printf("0");
        printf("\n");
    }
}
/*
1785934 2
1785934 3
10010010 3
10010010 2
56653536 5
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值