HDU 3183 A Magic Lamp (双向链表贪心删除 或者是 RMQ取数)

A Magic Lamp

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


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
 

 

Source
 

 

Recommend
lcy
 
 
方法一是我自己想出来的。就是每次删除数的时候都是删掉第一个比右边数大的数。
比如样例 178543  4
第一次删除第一个数的时候从左边找起。找到第一个a[i]>a[i+1]的数。删除8
17543
第二次一样的,删除7
1543
第三次删除5
143
第四次删除4
就得到13了。就是答案。
 
用双向链表,可以很方便的删除,速度也很快。
/*

HDU 3183

G++  0ms

双向链表,每次删除掉第一个比右边的数大的数

*/



#include<stdio.h>

#include<string.h>

#include<iostream>

#include<algorithm>

using namespace std;

const int MAXN=1010;

struct Node

{

    int from,to;

    int v;

}node[MAXN];

int tol;

void del(int t)//删除t结点

{

    int temp=node[t].from;

    node[temp].to=node[t].to;

    node[node[t].to].from=temp;

}

char str[MAXN];

int main()

{

    //freopen("in.txt","r",stdin);

    //freopen("out.txt","w",stdout);

    int m;

    while(scanf("%s%d",&str,&m)!=EOF)

    {

        int n=strlen(str);

        for(int i=1;i<=n;i++)

        {

            node[i].v=str[i-1]-'0';

            node[i].from=i-1;

            node[i].to=i+1;

        }

        node[0].to=1;

        node[n+1].from=n;

        int t=0;

        while(m--)

        {

            while(node[t].to!=n+1 && node[node[t].to].v>=node[t].v) t=node[t].to;

            del(t);

            t=node[t].from;

        }

        t=node[0].to;

        while(t!=n+1 && node[t].v==0)t=node[t].to;

        if(t==n+1)

        {

            printf("0\n");

            continue;

        }

        while(t!=n+1)

        {

            printf("%d",node[t].v);

            t=node[t].to;

        }

        printf("\n");

    }

    return 0;

}

 

 

 

第二种方法就是反过来考虑,取n-m个数,使得形成的数最小。

RMQ。

 

/*

HDU 3183

*/

#include<stdio.h>

#include<iostream>

#include<algorithm>

#include<math.h>

#include<string.h>

using namespace std;

const int MAXN=1010;

int a[MAXN];

int dp[MAXN][20];



void makeRMQIndex(int n,int b[])//形成最小值下标的RMQ

{

    for(int i=0;i<n;i++)

      dp[i][0]=i;

    for(int j=1;(1<<j)<=n;j++)

       for(int i=0;i+(1<<j)-1<n;i++)

          dp[i][j]=b[dp[i][j-1]]<=b[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];

          //这里一定要加等号,就是相等的时候取下标小的

}

int rmqIndex(int s,int v,int b[])

{

    int k=(int)(log(v-s+1.0)/log(2.0));

    return b[dp[s][k]]<=b[dp[v-(1<<k)+1][k]]?dp[s][k]:dp[v-(1<<k)+1][k];

    //加等号,取小标小的

}

char str[MAXN];

int ans[MAXN];

int main()

{

    //freopen("in.txt","r",stdin);

    //freopen("out.txt","w",stdout);

    int m;

    while(scanf("%s%d",&str,&m)!=EOF)

    {

        int n=strlen(str);

        for(int i=0;i<n;i++)a[i]=str[i]-'0';

        makeRMQIndex(n,a);

        int t=0;

        int temp=0;

        for(int i=m;i<n;i++)//找n-m个数,每次从[t,i]中找最小的

        {

            t=rmqIndex(t,i,a);

            ans[temp++]=a[t++];

        }

        t=0;

        while(t<temp&&ans[t]==0)t++;

        if(t>=temp)printf("0\n");

        else

        {

            for(int i=t;i<temp;i++)printf("%d",ans[i]);

            printf("\n");

        }

    }

    return 0;

}

 

 

 

你可能感兴趣的:(双向链表)