HDU - 4726 Kia's Calculation (贪心 数字加和错误运算处理)

本文介绍了一种特殊的加法计算方法,适用于对整数进行特定规则的数字混洗后再相加的场景。通过一种贪心算法策略,实现了在不引入前导零的情况下获得最大可能和的目标。

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

告诉起亚医生如何计算两个整数的总和。但起亚是这么粗心,总是两位数字超过9时忘记携带一个数字。例如,当她计算4567 + 5789时,她将得到9246,而1234 + 9876,她将得到0.酥油生气关于这个,为她解决了一个难题:
现在起亚有两个整数A和B,她可以按照自己喜欢的顺序对每个数字中的数字进行混洗,但是不允许前导零。也就是说,对于A = 11024,她可以将数字重新排列为10124或41102,或者其他很多,但不允许有02411。
在她洗牌之后,她将以自己的方式加在一起。 A“+”B的最大可能和是多少?
输入
第一行有一个数字T(T <= 25),表示测试用例的数量。
每个测试用例都有两行。第一行有数字A,第二行有数字B.
A和B都具有相同的数字位数,不大于10 6,并且没有前导零。
输出
对于测试用例X,首先输出“Case #X:”,然后输出最大可能的总和而不用前导零。
示例输入
1
5958
3036
示例输出

案例#1:8984

思路:贪心 

1.先排第一位数字,因为有先导0的存在,处理第一位的时候不考虑0

2.考虑特殊情况55+55=0等特殊数据的处理

3.计算数字的时候从是否满足9到0来计算

#include <algorithm>
#include<stdio.h>
#include<string.h>
using namespace std;

char str1[2000002],str2[2000002];
int a[20],b[20];
int ans[2000002];
int main()
{
    int t,icase=0;
    scanf("%d",&t);
    while(t--)
    {
        icase++;
        scanf("%s%s",str1,str2);
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        int num=strlen(str1);
        for(int c=0; c<num; c++) //转换数字完毕
        {
            a[str1[c]-'0']++;
            b[str2[c]-'0']++;
        }

        if(num==1)    //讨论只有一位数的情况
        {
            printf("Case #%d: %d\n",icase,(str1[0]-'0'+str2[0]-'0')%10);
            continue;
        }

        int imax=0,i=0,x,y;
        //因为先导0的存在 特别的算出第一位数字
        for(int itemp=1; itemp<= 9; itemp++)
            for(int ipos=1; ipos<=9; ipos++)
                if(a[itemp]&&b[ipos]&&((itemp+ipos)%10>=imax))
                {
                    imax=(itemp+ipos)%10;
                    x=itemp;
                    y=ipos;
                }
        a[x]--;
        b[y]--;
        ans[i++]=imax;

        for(int sum=9; sum>=0; sum--)  //数字从9到0循环
            for(int itemp=0; itemp<=9; itemp++)
            {
                if(a[itemp])
                {
                    //讨论两数相加<10
                    int ipos;
                    if(itemp<=sum)
                        ipos=sum-itemp;
                    //讨论两数相加>10
                    else
                    {
                        ipos=10+sum-itemp;
                        if(ipos>9) continue;
                    }
                    if(b[ipos])
                    {
                        int k=min(a[itemp],b[ipos]);
                        a[itemp]-=k;
                        b[ipos]-=k;
                        while(k--)
                            ans[i++]=sum;
                    }
                }
            }
        printf("Case #%d: ",icase);
        int cnt=0;
        while((ans[cnt]==0)&&(cnt<num-1))  //特殊情况 55 55
            cnt++;
        for(int i=cnt; i<num; i++)
            printf("%d",ans[i]);
        printf("\n");

    }
    return 0;
}



<think>好的,我现在需要仔细分析用户提供的这段循环代码,并理解其作用。用户提到在C语言中要避免使用方括号来访问数组元素,所以之前已经给出了修改后的代码,用指针算术替代了数组的[]操作符。现在用户又具体询问关于循环的部分:`for (int j = 1, k = 0; j < strlen(calculation[i]) - 1 && k < strlen(s); j++, k++)`。用户可能希望进一步了解这个循环的结构、作用以及如何在不使用方括号的情况下实现同样的功能。 首先,我需要拆解这个循环的各个部分。循环初始化了两个变量jk,分别初始化为10。循环条件是j小于`strlen(calculation[i]) - 1`且k小于`strlen(s)`。每次迭代后,jk都自增1。循环体内部比较了calculation[i][j]s[k],如果有不匹配的情况,就将flag设为false并跳出循环。 原循环的目的是逐个字符比较calculation数组中第i个字符串(从第1个字符开始,到倒数第二个字符结束)与字符串s的字符是否一致。这可能是在检查某个运算符是否匹配,比如calculation中的元素可能是像“(+)”这样的形式,而s可能是要匹配的运算符如“+”。所以需要跳过calculation[i]的首尾字符,比如括号,只比较中间的部分。 现在的问题是如何在不使用方括号的情况下重写这个循环。用户之前的问题是关于如何避免使用[],所以需要将所有的数组访问改为指针算术。例如,calculation[i][j]需要转换为使用指针的偏移来访问。 首先,需要理解calculation的数据类型。假设calculation是一个指向字符指针数组的指针,即char **calculation,那么calculation[i]等同于*(calculation + i),这是一个指向第i个字符串的指针。同样,s是一个字符指针,s[k]等同于*(s + k)。 在之前的回答中,用户已经将calculation[i][j]替换为*(current_cal + j),其中current_cal是*(calculation + i)。而s[k]则替换为*(s + k)。 现在针对这个循环部分,需要将循环中的条件表达式循环体内的数组访问都改为指针操作。同时,可能需要优化strlen的重复计算,因为每次循环条件判断都会调用strlen,这会导致性能问题,尤其是当字符串较长时。不过用户可能更关注正确性而非性能,但作为优化建议,可以预先计算这些长度。 接下来,我需要逐步思考如何重构这个循环: 1. **循环初始化**:jk分别初始化为10。这部分保持不变,因为变量初始化不涉及数组访问。 2. **循环条件**:原来的条件是j < strlen(calculation[i]) - 1 k < strlen(s)。这里需要将calculation[i]转换为指针形式,即current_cal = *(calculation + i),所以strlen(current_cal)就是原calculation[i]的长度。同时,s的长度可以预先计算并存储在一个变量中,比如s_len = strlen(s)。同样,current_cal_len = strlen(current_cal),这样循环条件中的j < current_cal_len - 1k < s_len会更高效。 3. **循环体内部**:比较calculation[i][j]s[k],即*(current_cal + j)*(s + k)。如果有不匹配,设置flag为false并break。 4. **循环后处理**:j++k++,继续逐个字符比较。 此外,需要注意原循环中的calculation[i]可能是一个类似“(operator)”的字符串,比如“(+)”,所以j从1开始,到strlen(calculation[i])-2的位置结束,即跳过首尾的字符。例如,如果calculation[i]是“(+)”,长度是3,所以j的范围是1到1(因为3-1=2,j < 2,所以j最大是1),即只比较中间的“+”字符。而s的长度如果是1的话,k的范围是0到0,即比较s的第一个字符。 可能的疑问点包括: - 为什么j从1开始?这可能是因为calculation中的运算符被包裹在其他字符中,比如括号,需要跳过这些符号,只比较中间的运算符部分。 - 是否需要在循环外预先计算字符串的长度,以避免重复调用strlen?是的,这样可以提高效率,尤其是在长字符串的情况下。 - 如何正确使用指针算术来替代数组索引?需要确保每个字符的访问都通过指针的偏移量来实现,比如*(current_cal + j)代替current_cal[j]。 在修改后的代码中,用户已经将calculation[i]替换为current_cal,即通过指针算术访问数组元素。现在需要将循环中的条件表达式字符访问也改为指针形式。同时,预先计算字符串的长度可以优化循环条件判断。 总结步骤: 1. 将calculation[i]转换为指针形式,并预先计算其长度。 2. 预先计算s的长度。 3. 在循环条件中使用预先计算的长度,避免重复调用strlen。 4. 在字符比较中使用指针偏移来替代数组索引。 现在需要将这些步骤整合到循环的重构中,并确保逻辑正确,同时避免使用方括号。</think>--- ### **代码段功能解析** 原代码段是**字符比对循环**,其核心逻辑为: ```c for (int j = 1, k = 0; j < strlen(calculation[i]) - 1 && k < strlen(s); j++, k++) { if (calculation[i][j] != s[k]) { flag = false; break; } } ``` 作用:逐个比较 `calculation` 中第 `i` 个字符串(**跳过首尾字符**)与输入字符串 `s` 的字符是否完全一致。 --- ### **关键点拆解** #### **1. 循环变量初始化** ```c int j = 1, k = 0 ``` - **`j = 1`**:从 `calculation[i]` 的**第二个字符**开始遍历(跳过首字符,例如假设 `calculation[i]` 格式为 `"(+)"`) - **`k = 0`**:从字符串 `s` 的**第一个字符**开始遍历 #### **2. 循环条件** ```c j < strlen(calculation[i]) - 1 && k < strlen(s) ``` - **`strlen(calculation[i]) - 1`**:计算 `calculation[i]` **有效内容长度**(跳过首尾字符) - **`k < strlen(s)`**:确保 `s` 的遍历不越界 - **逻辑**:同时满足两个条件才继续循环(字符未比对完且未发现不匹配) #### **3. 循环体** ```c if (calculation[i][j] != s[k]) { flag = false; break; // 发现不匹配立即退出 } ``` - **比对逻辑**:若任意对应位置字符不等,标记失败并终止比对 #### **4. 循环增量** ```c j++, k++ ``` - 同步递增两个索引,实现**逐字符对齐比对** --- ### **修改为指针操作后的等效代码** ```c // 预计算长度(优化性能) int len_cal = strlen(*(calculation + i)) - 2; // calculation[i] 的有效长度 int len_s = strlen(s); for (int j = 1, k = 0; j <= len_cal && k < len_s; // 直接使用预计算长度 j++, k++) { char c1 = *(*(calculation + i) + j); // calculation[i][j] char c2 = *(s + k); // s[k] if (c1 != c2) { flag = false; break; } } ``` --- ### **修改说明** 1. **指针替换方括号**: - `calculation[i][j]` → `*(*(calculation + i) + j)` - `s[k]` → `*(s + k)` 2. **性能优化**: - 预计算 `len_cal` `len_s`,避免循环中重复调用 `strlen` 3. **逻辑等价性**: - 原代码中 `j < strlen(calculation[i]) - 1` 等价于 `j <= len_cal`(因 `len_cal = strlen(...) - 2`) --- ### **场景示例** 假设 `calculation` 中某元素为 `"(+)"`,输入 `s = "+"`: 1. `len_cal = 3 - 2 = 1`(有效字符长度为 1) 2. 循环执行: - `j=1`, `k=0` → 比对 `calculation[i][1]`(即 `+`) `s[0]`(即 `+`),匹配成功 - `j++` → `j=2`,此时 `j > len_cal`(`1`),循环终止 3. 最终返回匹配成功 --- ### **注意事项** 1. **边界问题**:确保 `calculation[i]` 的格式为 `"运算符"`(首尾有额外字符) 2. **内存安全**:验证 `calculation` `s` 是否为合法字符串(以 `\0` 结尾) 3. **可读性**:指针操作建议配合注释,避免歧义 如果需要进一步探讨其他细节,欢迎继续提问!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值