Uva 7344 Numbered Cards (状压DP)

本文介绍了一种利用状态压缩动态规划解决特定数位DP问题的方法,具体为求解1到N范围内,所有子集中任意两数任意位不相同的情况总数。通过预处理和搜索策略,实现了高效求解。
/**
题意:从1 ~ N,选出若干个子集,满足子集里面任意两个数的任意两个数位没有相同的,一个数自身可以有相同的数位,求这样的子集的个数

思路:因为数字只有九个,考虑状压,先看状态为S的数(状态为0000000101代表只有2和0的数,比如200,20,220),设dp[S]:N以内子集状态为S
的数量,ans[S]:N以内的一个数的状态恰为S的数量,当选择状态为S的添加进集合时,那么有dp[T] += dp[K] * ans[S] (其中K交S为空集,
K|S为T),剩下就是计算ans[S],预处理出0~9,00~99,000~999...包含前导0的状态为S的数量res[S][1],res[S][2],res[S][3]....
剩下的是搜索,高位往低位搜索,当选择的位小于当前位的时候后面不用再去搜索了,因为怎么选择都可以,用res数组统计一下即可,当等于
的时候
**/
#include<bits/stdc++.h>
typedef long long ll;
const ll mod = 1e9 + 7;
const int maxn = 1200;
using namespace std;

struct P {
    int S, num;
    P() {}
    P(int s, int n) : S(s), num(n) {}
    bool operator < (P p) const {
        if(num != p.num) return num < p.num;
        return S < p.S;
    }
    void init() {
        int s = S; num = 0;
        while(s) {
            if(s & 1) num++;
            s >>= 1;
        }
    }
} p[maxn];
ll res[maxn][11], bit[11];
ll dp[maxn], ans[maxn];
int n, T, kase = 1;

void init() {
    memset(res, 0, sizeof res);
    for(int i = 0; i < 10; i++) res[1 << i][1] = 1;
    for(int i = 2; i < 11; i++) {
        for(int x = 0; x < 10; x++) {
            int S = 1 << x;
            for(int s = 0; s < 1024; s++) {
                int T = s | S;
                res[T][i] += res[s][i - 1];
                res[T][i] %= mod;
            }
        }
    }
    for(int i = 0; i < 1024; i++) {
        p[i] = P(i, 0);
        p[i].init();
    }
    sort(p, p + 1024);
}

int solve(int n) {
    int len = 0;
    while(n) {
        bit[++len] = n % 10;
        n /= 10;
    }
    return len;
}

void dfs(int l, int S, int limit) {
    if(!l) { ans[S]++; return ; }
    if(!limit && S > 1) {
        for(int x = 1; x < 1024; x++) {
            int T = x | S;
            ans[T] = (ans[T] + res[x][l]) % mod;
        }
        return ;
    }

    int up = limit ? bit[l] : 9;
    for(int i = 0; i < up; i++) {
        int newS = (S <= 1 && !i) ? 0 : S | (1 << i);
        dfs(l - 1, newS, 0);
    }
    int newS = (S <= 1 && !up) ? 0 : S | (1 << up);
    dfs(l - 1, newS, limit ? 1 : 0);
}

int main() {
    init();
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        int len = solve(n);
        memset(ans, 0, sizeof ans);
        memset(dp, 0, sizeof dp);
        dfs(len, 0, 1);
        dp[0] = 1;
        for(int i = 2; i < 1024; i++) {
            int S = p[i].S;
            for(int j = 1023; j >= 0; j--) {
                int T = p[j].S;
                if((S | T) != T) continue;
                int K = T & (~S);
                dp[T] = (dp[T] + dp[K] * ans[S] % mod) % mod;
            }
        }
        ll t = 0;
        for(int i = 2; i < 1024; i++) t += dp[i];
        printf("Case %d: %lld\n", kase++, t % mod);
    }
    return 0;
}

<think>好的,我现在需要解决用户提出的关于如何检测Word文档中的段落是否为编号列表的问题。用户给出的方法名是`is_numbered_list`,需要我提供代码实现并解释。首先,我需要理解Word文档中列表的存储方式。Word使用numId和ilvl来标识列表的编号和层级。每个列表项都有一个numPr(Numbering Properties)元素,其中包含numId和ilvl。numId对应的是文档中定义的编号样式,而ilvl表示列表的层级。接下来,我需要确定如何判断一个列表是否为编号列表(有序列表)还是项目符号列表(无序列表)。这需要访问Word文档中的编号定义部分,即NumberingDefinitionsPart。每个numId对应一个抽象编号(AbstractNum),而抽象编号中定义了列表的类型,比如是否是编号、项目符号或多级列表。因此,步骤应该是: 1.获取当前段落的numId。 2.根据numId找到对应的抽象编号定义。 3.检查该抽象编号的编号格式(如数字、字母、符号等)。在Python的python-docx库中,直接访问底层XML元素是必要的,因为高级API可能没有暴露这些信息。需要解析document.xml.rels中的NumberingDefinitionsPart,并遍历其中的AbstractNum元素。 考虑到性能问题,应该缓存已经解析过的编号定义,避免重复解析。同时,需要处理可能的异常,例如numId不存在或对应的抽象编号未找到的情况。然后,我需要编写代码来实现这一过程。代码需要包括:-从段落中提取numId。 -加载文档的编号定义部分。 -查找对应的抽象编号。-检查抽象编号的编号格式是否为数字类型。在实现过程中,需要注意XML命名空间的处理,因为Word文档的XML元素使用了特定的命名空间。使用XPath表达式来查询元素会更加高效。此外,需要处理多级列表的情况,每个层级的编号格式可能不同。但根据用户的问题,当前只需要判断整个列表是否为编号列表,可能只需检查第一层级的格式。最后,测试代码以确保正确识别不同类型的列表,并处理各种边界情况,如缺失numId、无效的numId等。</think>以下是实现`is_numbered_list`方法的完整代码及解析: ```python def is_numbered_list(self, num_id): """判断是否为编号列表(有序列表)""" if not num_id: return False # 获取文档的编号定义部分 numbering_part = self.doc.part.numbering_part numbering_definitions = numbering_part.numbering_definitions # 遍历所有编号定义 for num in numbering_definitions: if num.numId == num_id: # 获取对应的抽象编号定义 abstract_num = num.abstractNumId # 在抽象编号定义中查找编号格式 for elem in abstract_num._element.xpath('.//w:lvl'): num_fmt = elem.xpath('w:numFmt/@w:val') if num_fmt and num_fmt[0] in ['decimal', 'upperLetter', 'lowerLetter']: return True return False # 需要添加的XML命名空间映射 NSMAP = { 'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' } ``` 实现原理说明: 1. **编号定义解析**: - 通过`numbering_part`访问文档的编号定义 - 每个`num`对应一个具体的编号实例 - `abstractNumId`关联到抽象的编号模板 2. **编号格式检测**: - 使用XPath定位层级定义(`w:lvl`) - 检查`w:numFmt`属性值是否为数字类型格式 - 支持检测的格式包括:十进制数字(decimal)、大写字母(upperLetter)、小写字母(lowerLetter) 3. **性能优化**: ```python # 添加缓存机制 from functools import lru_cache @lru_cache(maxsize=100) def is_numbered_list(self, num_id): # 原有代码... ``` 4. **错误处理增强**: ```python def is_numbered_list(self, num_id): try: if not num_id or not self.doc.part.numbering_part: return False # 原有代码... except (AttributeError, KeyError) as e: print(f"编号检测错误: {str(e)}") return False ``` 验证测试用例: ```python # 测试不同列表类型的检测结果 test_cases = [ ("1.", True), # 数字编号 ("A.", True), # 大写字母 ("a.", True), # 小写字母 ("•", False), # 项目符号 ("■", False), # 图形符号 ("①", True) # 特殊数字 ] ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值