1024. Palindromic Number (25)

本文提供了一道 PAT-A 1024 编程题的解答思路及 C++ 实现代码。该题要求通过不断翻转字符串并进行加法运算直至得到回文数,同时记录操作次数。

题目链接:https://www.patest.cn/contests/pat-a-practise/1024


#include<bits/stdc++.h>
const int maxn =10100;
using namespace std;
string s1,s2;
int num1[11],num2[11];
string fun(string s){
    string ans,s2=s;
    int n=s.length();
    reverse(s2.begin(),s2.end());
    int t=0;
    int temp=0;
    char c;
    for(int i=0;i<n;i++){
        temp=(int)(s[i]-'0'+s2[i]-'0')+t;
        if(temp>=10){
            t=1;
            temp%=10;
            c=temp+'0';
            ans+=c;
        }
        else{
            t=0;
            c=temp+'0';ans+=c;
        }
    }
    if(t)ans+=(t+'0');
    reverse(ans.begin(),ans.end());

    return ans;
}
int k;
int main(){
    cin>>s1>>k;
    int cnt=0;
    int flag=1;
    s2=s1;
    reverse(s2.begin(),s2.end());
    if(s2==s1)flag=0;
    while(k--&&flag){
        cnt++;
        s1=fun(s1);
        s2=s1;
        reverse(s2.begin(),s2.end());
        if(s2==s1)break;
    }
    cout<<s1<<endl;
    cout<<cnt<<endl;
    return 0;
}

这是一个 **子集和问题的变种**:我们需要统计所有非空子集(也可以是空集?看样例),使得子集元素之和是一个**回文数(palindromic number)**。 --- ## ✅ 题意解析 - 给定 `N` 个正整数(可能重复) - 你可以选择任意数量的数(1 到 N 个,即非空子集) - 求有多少种**选择方式(子集)**,其和为**回文数** - 子集不同 ⇨ 即使和相同也算不同(只要选的元素位置不同就算不同) 但注意:输入样例中: ``` 输入: 3 1 1 100 输出:3 ``` 我们来分析这个样例。 --- ## 🔍 样例分析:`[1, 1, 100]` 所有非空子集有 $2^3 - 1 = 7$ 个: | 子集 | 元素 | 和 | 是否回文 | |------|------|----|---------| | {1} | [1] | 1 | ✅ 是 | | {1} | [1] | 1 | ✅ 是 | | {100} | [100] | 100 | ❌ 否("100" ≠ "001") | | {1,1} | [1,1] | 2 | ✅ 是 | | {1,100} | [1,100] | 101 | ✅ 是 | | {1,100} | [1,100] | 101 | ✅ 是 | | {1,1,100} | [1,1,100] | 102 | ❌ 否 | 现在检查每个和是否是回文数: - 1 → 是 - 1 → 是 - 100 → 不是 - 2 → 是 - 101 → 是 - 101 → 是 - 102 → 不是 所以满足条件的子集有: 1. 第一个 1 → 和=1 2. 第二个 1 → 和=1 3. {1,1} → 和=2 4. {1,100}(第一个1+100)→ 和=101 5. {1,100}(第二个1+100)→ 和=101 共 **5 个** 但输出是 **3**! 这说明我们的理解有问题。 --- ## ❗ 重新思考:是不是相同的值视为同一种选择? 或者题目中的“ways”是指不同的**和值**? 但题目说:“how many ways you can choose the numbers”,应该是不同的子集算不同的方式。 然而输出是 `3`,不是 `5`。 ### 可能原因:题目认为两个 `1` 是不可区分的? 即数组 `[1,1,100]` 中有两个相同的 `1`,如果它们来自不同位置但值相同,在组合中视为相同? 但这与常规“子集计数”不符。 除非题目意思是:**不同的子集,即使元素相同、位置不同,但如果值一样,就合并?** 不,通常不会这样。 再试另一种可能性:我们列出所有**不同的和值**中,哪些是回文? - 和=1 → 回文 ✅ - 和=2 → 回文 ✅ - 和=101 → 回文 ✅ - 其他:100,102 → 不是 → 有 3 个**不同的回文和值** 但题目问的是“多少种选择方式”,不是“多少个回文和” → 所以不是这个解释。 --- ## 🚫 矛盾!我们得到 5,样例输出是 3 ### 新思路:也许题目不允许选单个数字?但样例用了单个 1 等等,再看一遍题目: > "you can choose 1,2,3,....or n of them" → 至少选一个 → 正确 > 输出是 3 只有当我们把两个 `1` 视为同一个,并且只算一次和=1 的情况时才可能。 但即便如此,还有: - 和=1(出现两次,但只算一次?) - 和=2({1,1}) - 和=101(出现两次,但只算一次?) → 如果按**不同的子集**算,是 5 → 如果按**不同的和值**算,是 3(1,2,101) → 所以很可能:**题目在问有多少个不同的回文和值?** 但题目原文是: > how many ways you can choose the numbers so that the sum of them is palindromic number. 这里的 “ways” 应该是子集的数量,而不是不同和的数量。 --- ## ✅ 正确解读:可能是 **LeetCode 类型的“子集去重”问题** 即:虽然有两个 `1`,但因为值相同,某些子集被视为相同? 例如,在集合 `{1,1,100}` 中,选择第一个 `1` 或第二个 `1` 被视为同一种方式? 但在标准算法题中,如果是**索引不同**,就是不同的子集。 除非题目明确说是“distinct subsets by value”。 但我们来看另一个角度:最大可能和是多少? - N ≤ 20,ai ≤ 100 → 最大和 = 20×100 = 2000 - 回文数范围小:从 1 到 2000 的回文数大约只有几百个 我们可以用 **折半搜索(Meet-in-the-Middle) + 回文判断** 来暴力枚举所有子集和,然后统计和为回文数的子集数量。 然后运行样例看看。 --- ## ✅ 重新计算样例(假设两个 1 是可区分的) 子集如下(设 a=1, b=1, c=100): 1. a → sum=1 ✅ 2. b → sum=1 ✅ 3. c → sum=100 ❌ 4. ab → sum=2 ✅ 5. ac → sum=101 ✅ 6. bc → sum=101 ✅ 7. abc → sum=102 ❌ → 总共 5 个满足条件 但输出是 3 ### 唯一可能:题目意思是——输出的是**有多少个不同的回文和值**? 即: - 和=1 → 回文 - 和=2 → 回文 - 和=101 → 回文 共 3 个不同的回文和值 → 输出 3 ✅ 这就对上了! --- ## ✅ 最终理解 题目问的是: > 有多少个**不同的和值 S**,使得存在某个非空子集,其和为 S,且 S 是回文数? 即:不是统计子集个数,而是统计**能构成的回文和值的种类数** 例如: - 和=1 可以构成 → 计 1 次 - 和=2 可以构成 → 计 1 次 - 和=101 可以构成 → 计 1 次 - 和=100 不行 → 不计 总数 = 3 --- ## ✅ 解法步骤 1. 使用位掩码或 DFS 枚举所有非空子集的和 2. 将所有可能的和存入一个集合(set)中 3. 遍历这个集合,判断每个和是否是回文数 4. 统计回文和的数量 由于 N ≤ 20,最多 $2^{20} \approx 1e6$ 种子集,完全可行 --- ## ✅ C++ 实现 ```cpp #include <iostream> #include <vector> #include <set> #include <string> #include <algorithm> using namespace std; // 判断一个数是否是回文数 bool isPalindrome(int n) { string s = to_string(n); string t = s; reverse(t.begin(), t.end()); return s == t; } int main() { int N; while (cin >> N) { vector<int> nums(N); for (int i = 0; i < N; ++i) { cin >> nums[i]; } set<int> possibleSums; // 枚举所有非空子集 for (int mask = 1; mask < (1 << N); ++mask) { int sum = 0; for (int i = 0; i < N; ++i) { if (mask & (1 << i)) { sum += nums[i]; } } possibleSums.insert(sum); } // 统计其中有多少个是回文数 int count = 0; for (int s : possibleSums) { if (isPalindrome(s)) { count++; } } cout << count << endl; } return 0; } ``` --- ## ✅ 再验证样例 输入:`[1, 1, 100]` 所有非空子集和: - 1 (from first 1) - 1 (second 1) → 已存在 - 100 - 1+1=2 - 1+100=101 - 1+100=101 → 已存在 - 1+1+100=102 → 集合:{1, 2, 100, 101, 102} 回文判断: - 1 → 是 - 2 → 是 - 100 → 否 - 101 → 是 - 102 → 否 → 共 3 个回文和值 → 输出 `3` ✅ --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值