HDU 4351 (水过的方法)

本文详细介绍了如何通过状态压缩和优化算法,如构造next数组跳过连续0,来高效解决HDU4351问题。文中包括算法原理、代码实现以及性能对比分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4351

 

        题意就不描述了,这道题目的标准做法应该是区间合并的线段树,因为比赛的时候想到了能水过的方法,赛后证明是可行的,特意记录说明一下。

        题目说明 0 <= Ai <= 1e9 ,为了方便讨论暂时不管 Ai = 0 的情况,这样对于每次查询 [l, r],由于子区间数量增长很快,而有效的状态只有10个,所以枚举的过程中状态 9、8、7、6、5 很快就会出现,这些状态一旦出现就可以立即终止枚举。

        如果数据中有大量连续的0,易知枚举必定会TLE,因此需要构造一个 next[] 数组,作用就是跳过当前元素前面的所有连续 0。此外因为只有10个状态,可以用状态压缩来优化时间效率。

        这样一来,这道题目就能完美水过了,比赛的时候对这种方法信心不足导致TLE一次后就放弃了,自信不足。晚点再用线段树的方法做一遍。

        经测试,不用 next[] 数组跳过连续 0 的枚举要 600+ms(如果数据强应该会TLE的),加了这个优化后 200+ms 能跑完,此外是否用状态压缩对时间效率没影响,总的来看这种水水的方法无论时间、空间、代码量上都应该比线段树的写法有优势(我没具体提交别人的线段树代码测时间效率,但据说线段树写法不用状态压缩还过不了),上面所有测试结果都是在下面这份代码上修改的。

/****************************************************************
	Problem: HDU 4351
	User: Jeflie
	Language: C++
	Result: Accepted
	Time: 218 ms
	Memory: 1424 kb
****************************************************************/
 
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 100010;
 
int dig[N], next[N];
int sum[N];
 
int Digit(int x)
{
    return (x - 1) % 9 + 1;
}
 
void Cal(int l, int r)
{
    int judge = 0;
    int target = (1<<5) | (1<<6) | (1<<7) | (1<<8) | (1<<9);
    for (int j = l; j <= r; j = next[j])
    {
        for (int k = l; k <= j; k = next[k])
        {
            if (k < r && next[k] != k+1)  // 检测是否跳过0
                judge |= 1;
            int tmp = Digit(sum[j] - sum[k-1]);
            judge |= (1 << tmp);
            if ((judge & target) == target)
            {
                printf("9 8 7 6 5\n");
                return;
            }
        }
    }
 
    bool flag = 0;
    int cnt = 5;
    for (int i = 9; i >= 0; i--)
    {
        if ((judge&(1<<i)) && cnt)
        {
            if (flag) printf(" ");
            printf("%d", i);
            cnt--;
            flag = 1;
        }
    }
 
    while (cnt--)
    {
        if (flag) printf(" ");
        printf("-1");
        flag = 1;
    }
    printf("\n");
}
 
int main()
{
    int T, Case = 1;
    scanf("%d", &T);
    while (T--)
    {
        sum[0] = 0;
        int n, tmp;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &dig[i]);
            dig[i] = Digit(dig[i]);
            sum[i] = sum[i-1] + dig[i];
        }
 
        tmp = n+1;
        for (int i = n; i > 0; i--)   // 用next数组跳过元素前面的连续0
        {
            next[i] = tmp;
            if (dig[i]) tmp = i;
        }
 
        printf("Case #%d:\n", Case++);
        int k, l, r;
        scanf("%d", &k);
        while (k--)
        {
            scanf("%d %d", &l, &r);
            Cal(l, r);
        }
        if (T) printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值