HDU 3183 A Magic Lamp 【RMQ 递归 求解】

3 篇文章 0 订阅

题目链接

题目大意:给你一个1000内长度数字让你删掉m个数使得剩下的数值最小


思路:删掉m个数,那么剩下n-m个数。最高为越小,整个数就越小,即使后面再大,比如1999和2000,所以要让小的数在高位。

用RMQ找出整个区间的最小值,然后从最小值右边区间找最小值,然后递归寻找右边区域最小值。

右边区域找完如果还没达到n-m只能找左边的区域最小值。反复递归。比如54123,先找到1,然后再找到2,然后3,然后4,然后5。

找到的数用ans数组记录,最后遍历ans数组输出。


WA了四次;因为长度太长,用了一个int来存输出结果,就WA了

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string>
#include <algorithm>
#include <string.h>
#define N 1100
using namespace std;

int ans[N],n,m;

struct node
{
    int x,i;
} a[N][30];

void RMQ()
{
    for(int j = 1; (1<<j) <= n; j++)
        for(int i = 1; i+(1<<j)-1 <= n; i++)
            if(a[i][j-1].x <= a[i+(1<<(j-1))][j-1].x)
            {
                a[i][j].x = a[i][j-1].x;//找到最小值,并且存下标
                a[i][j].i = a[i][j-1].i;
            }
            else
            {
                a[i][j].x = a[i+(1<<(j-1))][j-1].x;
                a[i][j].i = a[i+(1<<(j-1))][j-1].i;
            }

}

void solve(int l,int r)
{
    if(!m || l > r)//如果l>r越界,然会上一层,m==0表示找到所有数
        return;
    int k = (int)(log10(r-l+1)/log10(2));
    int num1 = a[l][k].x;//数字
    int num2 = a[r-(1<<k)+1][k].x;
    int x1 = a[l][k].i;//数字对应的下标
    int x2 = a[r-(1<<k)+1][k].i;
    if(num1 <= num2)
    {
        ans[x1] = a[x1][0].x;//找到区间最小值,把它存入对应下标的ans中
        m--;//m减1
        solve(x1+1,r);
        if(m!=0)//找完右边找左边
            solve(l,x1-1);
    }
    else
    {
        ans[x2] = a[x2][0].x;
        m--;
        solve(x2+1,r);
        if(m!=0)
            solve(l,x2-1);
    }
}

int main()
{
    char ch[N];
    while(~scanf(" %s", ch))
    {
        memset(ans,-1,sizeof(ans));
        memset(a,0,sizeof(a));
        scanf("%d", &m);
        n = strlen(ch);
        m = n - m;//要剩下m个数
        if(m <= 0)//如果去掉的数比给的数多 为0
        {
            cout<<'0'<<endl;
            continue;
        }
        for(int i = 0; i < n; i++)
        {
            a[i+1][0].x = ch[i] - '0';//a下标从1开始
            a[i+1][0].i = i+1;
        }

        RMQ();
        solve(1,n);

        int flag = 0;
        int i = 1;
        while(ans[i] ==0 || ans[i] == -1)
            i++;
        for(; i <= n; i++)
            if(ans[i] != -1)
            {
                printf("%d",ans[i]);
                flag = 1;
            }
        if(!flag)
            cout<<0;
        printf("\n");
    }
    return 0;
}



WA代码:

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string>
#include <algorithm>
#include <string.h>
#define N 1100
using namespace std;

int ans[N],n,m;

struct node
{
    int x,i;
} a[N][30];

void RMQ()
{
    for(int j = 1; (1<<j) <= n; j++)
        for(int i = 1; i+(1<<j)-1 <= n; i++)
            if(a[i][j-1].x <= a[i+(1<<(j-1))][j-1].x)
            {
                a[i][j].x = a[i][j-1].x;//找到最小值,并且存下标
                a[i][j].i = a[i][j-1].i;
            }
            else
            {
                a[i][j].x = a[i+(1<<(j-1))][j-1].x;
                a[i][j].i = a[i+(1<<(j-1))][j-1].i;
            }

}

void solve(int l,int r)
{
    if(!m || l > r)//如果l>r越界,然会上一层,m==0表示找到所有数
        return;
    int k = (int)(log10(r-l+1)/log10(2));
    int num1 = a[l][k].x;//数字
    int num2 = a[r-(1<<k)+1][k].x;
    int x1 = a[l][k].i;//数字对应的下标
    int x2 = a[r-(1<<k)+1][k].i;
    if(num1 <= num2)
    {
        ans[x1] = a[x1][0].x;//找到区间最小值,把它存入对应下标的ans中
        m--;//m减1
        solve(x1+1,r);
        if(m!=0)//找完右边找左边
            solve(l,x1-1);
    }
    else
    {
        ans[x2] = a[x2][0].x;
        m--;
        solve(x2+1,r);
        if(m!=0)
            solve(l,x2-1);
    }
}

int main()
{
    char ch[N];
    while(~scanf(" %s", ch))
    {
        memset(ans,-1,sizeof(ans));
        memset(a,0,sizeof(a));
        scanf("%d", &m);
        n = strlen(ch);
        m = n - m;//要剩下m个数
        if(m <= 0)//如果去掉的数比给的数多 为0
        {
            cout<<'0'<<endl;
            continue;
        }
        for(int i = 0; i < n; i++)
        {
            a[i+1][0].x = ch[i] - '0';//a下标从1开始
            a[i+1][0].i = i+1;
        }

        RMQ();
        solve(1,n);
	//WA
        int num = 0;
        for(int i = 1; i <= n; i++)
            if(ans[i] != -1)
                num = num*10 + ans[i];
        printf("%d\n",num);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值