【RMQ】A Magic Lamp

A Magic Lamp

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2327    Accepted Submission(s): 923


Problem Description
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
 
题目大意:
  输入一串数字,然后在输入K,问你如果在这串数字中去除K位,则这串数字的最小值是多少、
 
解法:
1.RMQ (真的没想到Orz,Orz,Orz....)
  - -,如果没有想到该怎么用RMQ比较好,还是对RMQ的使用理解的不够深刻呀。。。
  对于每次输入M长度的数字串,要去除K为,那么得出结果的数字串是否最多为M-K个数字。我们依次对区间[s1,E],取最小值,然后再从这次最小值的位置的下一位开始s1开始,第二次从区间[s2,E+1]中在取最小值,反复操作,直到第M-K次,取得区间[s(M-K) ,M]的最小值,每一次区间所取得的最小值为所组成的数字串为最优解。
 si为第i-1次所取得的最小值的下一个位置。s1的初始值为1,E的初始值为K+1,每取一次E++(保证结果的每一位数都为最小值,且顺序不会错乱。)。

代码:(2015.8.15)

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <math.h>
 5 #include <stack>
 6 #define MAX 1010
 7 using namespace std;
 8 char Str[MAX];
 9 char Min[MAX][11];
10 void Cread_ST(int N)
11 {
12     for(int i=1;i<=N;i++)Min[i][0]=Str[i-1]-'0';
13     int Len=(int)(log(N*1.0)/log(2.0));
14     for(int j=1;j<=Len;j++){
15         for(int i=1;i+(1<<j)-1<=N;i++){
16             int TMD=i+(1<<(j-1));
17             Min[i][j]=min(Min[i][j-1],Min[TMD][j-1]);
18         }
19     }
20 }
21 int RMQ(int a,int b)
22 {
23     int K=(int)(log(b-a+1.0)/log(2.0));
24     int TMD=b-(1<<K)+1;
25     int MIN=min(Min[a][K],Min[TMD][K]);
26     return MIN;
27 }
28 int main()
29 {
30     int K,i,j,Len,Start,End,Zero,k;
31     int Num;
32     while(scanf(" %s %d",Str,&K)!=EOF)
33     {
34         Len=strlen(Str);
35         Cread_ST(Len);
36         k=Len-K;Start=1;Zero=1;End=K;
37         if(k<=0){printf("0\n");continue;}
38         while((k--)&&(Start<=Len)&&(End+1<=Len))
39         {
40             End++;
41             Num=RMQ(Start,End);
42             if(Num!=0)Zero=0;/*去除前导0*/
43             if(!Zero)printf("%d",Num);
44             for(j=Start;j<=End;j++)
45             {
46                 if(Str[j-1]-'0'==Num)
47                 {Start=j+1;break;}
48             }
49         }
50         if(Zero)printf("0");/*输入全部为0的情况*/
51         putchar(10);
52     }
53     return 0;
54 }
View Code

 

2.贪心的策略的(第一反应想到的。。。)
  对于每次去除数字,要使得这串数字最小,则首先考虑的是去除高位的数。
  比如 xx87xx,的话,我们如果去除8的话,得到的是xx7xx,如果我们不去除8的话,则无论去除后面哪一位都是xx8xx,而且这个数肯定会大于xx7xx,所以我们可以考虑先把当前数字比下一位大的话,优先去除。总的来说,就是每次从高位往下,判断如果一个数比它下一个数大的话,则去除这个数可以得到最优解,每去除一次后,从头开始从新判断即可。
  用数组做的话,每次去除一个数的话,不好操作,反而,用链表的话,则可以很好的实现这些功能,把数字一个一个链接起来。- -,自己闲的蛋疼,想到前向星是用头插法,自己也不太懂得用链表,然后就抱着试一试的态度,把前向星改成了链表,而且还是尾插法(想法很简单,不过链表实现起来,还是有些细节得注意的,Orz,Orz,Orz,....)。
代码:(2015.8.14)
 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #define MAX 1010
 5 using namespace std;
 6 char Num[MAX];
 7 int Top;/*记录边的编号*/
 8 struct edge{int P,TO,Next,Fa;}ID[MAX];
 9 void Add_E(char x,char y)/*尾插法*/
10 {
11     ID[Top-1].Next=Top;
12     ID[Top].P=x-'0';
13     ID[Top].TO=y-'0';
14     ID[Top].Fa=Top-1;
15     ID[Top++].Next=-1;
16 }
17 void Deal_Cread(int Len)/*初始化*/
18 {
19     memset(ID,-1,sizeof(ID));Top=1;
20 }
21 void Deal_Link(int Len)
22 {
23     for(int i=0,Max=Num[0]-'0',Max_S=1;i+1<Len;i++)
24     {
25         /*Max=Num[0]-'0';     去除前导0
26         if(Max_S&&MAX==0)continue;
27         else Max_S=0;*/
28         if(i==0)    /*获取第一个非0字符*/
29         {
30             ID[Top].Fa=-1;/*头节点的祖先设置为-1*/
31             ID[Top].P=Num[i]-'0';
32             ID[Top].TO=Num[i+1]-'0';
33             ID[Top++].Next=-1;
34             Max_S=1;
35         }
36         else Add_E(Num[i],Num[i+1]);
37     }ID[Top-1].Next=0;/*额外在尾部添加最后一个节点,方便操作*/
38      ID[0].Fa=Top-1;ID[0].Next=-1;
39      ID[0].P=Num[Len-1]-'0';ID[0].TO=9;
40 }
41 void Update(int i)
42 {
43     if(ID[i].Fa==-1)Top=ID[i].Next;/*如果去除点为头结点则更新头结点编号*/
44     ID[ID[i].Next].Fa=ID[i].Fa;
45     if(ID[i].Fa!=-1)
46     {
47         ID[ID[i].Fa].Next=ID[i].Next;
48         ID[ID[i].Fa].TO=ID[i].TO;
49     }
50 }
51 
52 void Deal_Del(int K)
53 {
54     Top=1;/*头结点的编号*/
55     while(K--)
56     {
57         int Sign,i;
58         for(i=Top;i!=-1;i=ID[i].Next)
59         {
60             Sign=ID[i].Fa;/*记录最后一个节点的父亲节点*/
61             if(ID[i].P>ID[i].TO){Update(i);break;}
62         }/*如果没有找到符合条件的情况,删除最后一个字符*/
63         if(i==-1)Update(ID[Sign].Next);
64     }
65 }
66 void Deal_Put()
67 {
68     int i,Sign;
69     for(i=Top,Sign=0;i!=-1;i=ID[i].Next)
70     {
71         if(!Sign&&Sign<ID[i].P)
72         {
73             Sign=ID[i].P;
74             printf("%d",ID[i].P);
75         }
76         else if(Sign)  printf("%d",ID[i].P);
77     }
78     if(!Sign)printf("0");
79     putchar(10);
80 }
81 int main()
82 {
83     int Len,K;
84     while(scanf(" %s %d",Num,&K)!=EOF)
85     {
86         Len=strlen(Num);
87         if(K>=Len){printf("0\n");continue;}
88         Deal_Cread(Len);
89         Deal_Link(Len);
90         Deal_Del(K);
91         Deal_Put();
92     }
93     return 0;
94 }
View Code

 PS: 如果不用链表的话,其实也可以直接用栈/队列来实现。

posted @ 2015-08-15 00:18  Wurq  阅读(164)  评论(0编辑  收藏  举报