hdu6321 C. Dynamic Graph Matching

本文介绍了一种使用状压动态规划方法解决给定动态无向图中匹配问题的算法实现。针对一个包含n个节点的无向图,在进行多次边的增删操作后,快速计算出所有可能的匹配方案数量,特别是当n较小(最大10)时,通过状态压缩的方法高效求解。

问题描述:
给定一个 n 个点的无向图,m 次加边或者删边操作。在每次操作后统计有多少个匹配包含 k = 1, 2, …,n/2 条边。
2 ≤ n ≤ 10, 1 ≤ m ≤ 30000。

官方解释
这里写图片描述

队友的代码,注释蛮详细的。。。

/** http://acm.hdu.edu.cn/showproblem.php?pid=6321
  * 题意:给n个点,求从中取没有公共点的几条边的方案数目
  * 动态添加或删除图中的边,每次改动,都依次输出取1到n/2条边的方案数
  * 思路:n小于等于10,不一定是允许暴搜,其实是暗示状压dp,
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
const int N = 1025;
const int M = 12;

int Kase,n,q,u,v,mxs;
int dp[N][M];
int cnt[N];//统计cnt[state]有几个1(点
char op[2];


int main() {
    for(int i=1;i<N;i++)
        cnt[i]=cnt[i>>1]+(i&1);
        //预处理统计所有状态有几个点,也用了dp的思维

    scanf("%d",&Kase);
    while(Kase--){
        scanf("%d %d",&n,&q);
        memset(dp,0,sizeof dp);
        mxs=(1<<n)-1;

        for(int i=0;i<=mxs;i++)
            dp[i][0]=1;

        while(q--){
            scanf("%s%d%d",op,&u,&v);
            u--,v--;
            int uv=(1<<u)|(1<<v);//把u和v点转换成二进制数uv,表示含有u和v点的状态

            for(int s=mxs;s>=0;s--){
                //从满状态开始更新,从点多到点少更新,避免重复累加
                if((s&uv)!=uv)continue;//不包括u和v点的状态跳过不更新

                for(int j=1;j<=cnt[s];j++){//这里和方向应该没关系,实际情况好像反过来会慢一些
                    dp[s][j]+=(op[0]=='+')?dp[s^uv][j-1]:(mod-dp[s^uv][j-1]);
                        //状态转移方程,当前状态(包括uv点的集合s,取j条边的的方案数)
                        //等于所有不包括uv点的集合,取j-1条边的方案数转移而来

                    if(dp[s][j]>=mod)dp[s][j]-=mod;
                       //这题会卡取模,以防万一用减法避免
                }
            }

            for(int i=1;i<=n/2;i++)
                printf("%d%c",dp[mxs][i],i==n/2?'\n':' ');
        }
    }
    return 0;
}
/*
1
4 8
+ 1 2
+ 3 4
+ 1 3
+ 2 4
- 1 2
- 3 4
+ 1 2
+ 3 4
*/
HDU 6259 是一道与回文子串相关的编程题目,要求统计特定条件下回文子串的数量。题目通常涉及字符串操作、动态规划或 Manacher 算法等技术。 ### 解题思路 题目核心在于识别并统计满足特定条件的回文子串。通常的解题方法包括: - **暴力枚举**:适用于小规模输入,时间复杂度为 $ O(n^2) $。 - **动态规划**:使用二维数组 `dp[i][j]` 表示从索引 `i` 到 `j` 的子串是否为回文。 - **Manacher 算法**:线性时间复杂度 $ O(n) $ 的高效算法,适用于大规模输入。 ### 示例代码 以下是一个使用动态规划方法统计所有回文子串的示例代码: ```cpp #include <iostream> #include <vector> #include <string> using namespace std; int countPalindromicSubstrings(string s) { int n = s.size(); vector<vector<bool>> dp(n, vector<bool>(n, false)); int count = 0; // 单个字符的回文 for (int i = 0; i < n; ++i) { dp[i][i] = true; ++count; } // 两个字符的回文 for (int i = 0; i < n - 1; ++i) { if (s[i] == s[i + 1]) { dp[i][i + 1] = true; ++count; } } // 更长的回文子串 for (int length = 3; length <= n; ++length) { for (int i = 0; i <= n - length; ++i) { int j = i + length - 1; if (s[i] == s[j] && dp[i + 1][j - 1]) { dp[i][j] = true; ++count; } } } return count; } int main() { string s; cin >> s; cout << countPalindromicSubstrings(s) << endl; return 0; } ``` ### 时间与空间复杂度分析 - **动态规划**:时间复杂度为 $ O(n^2) $,空间复杂度为 $ O(n^2) $。 - **Manacher 算法**:时间复杂度为 $ O(n) $,空间复杂度为 $ O(n) $。 ### 优化建议 对于大规模字符串(如长度超过 $ 10^5 $),应优先使用 Manacher 算法以提升效率。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值