P3736 [HAOI2016]字符合并 题解

状压DP优化
本文介绍了一种基于状态压缩的动态规划方法,通过优化减少时间复杂度。针对特定问题提出了一种新的枚举策略,避免重复计算,实现了高效求解。

发现 2≤k≤82 \leq k \leq 82k8,于是可以考虑状压 DP。

我们可以设 f[l][r][S]f[l][r][S]f[l][r][S] 表示区间 [l,r][l, r][l,r] 经过若干次操作变成 01 串 SSS 的最大分数。

基于贪心的思想,我们一定要使得整个 01 串最终不可操作,所以 01 串 SSS 的长度不超过 k−1k - 1k1,否则 SSS 可以继续进行合并,直到其长度小于等于 k−1k - 1k1

那么我们有一个很暴力的做法:

对于 l,rl, rl,r,枚举中点 midmidmid,再枚举区间 [l,mid][l, mid][l,mid][mid+1,r][mid + 1, r][mid+1,r] 的所有状态,同时枚举合并的区间 [p,p+k−1][p, p + k - 1][p,p+k1],直接转移即可。

这样做的时间复杂度是 O(kn322k−2)O(kn^32^{2k - 2})O(kn322k2) 的,会 T 飞。

这个时候,一般的想法是找到一些有利于统计的性质


我们会发现,一个 ppp 可能会被多个区间枚举到,但是它们转移的效果是一样的

举个例子:

假设我们有两个区间 [l1,r1],[l2,r2][l_1, r_1], [l_2, r_2][l1,r1],[l2,r2]

前者枚举出的状态分别为:00010,1010000010, 1010000010,10100

后者枚举出的状态分别为:00101,0100000101, 0100000101,01000

如果前者取区间 [4,8][4, 8][4,8] 合并,后者取区间 [3,7][3, 7][3,7] 合并,那么上面的两个区间合并出来是等价的。

所以我们遵从最简化原则,对于每个区间,我们钦定后面一部分区间(即 [mid+1,r][mid + 1, r][mid+1,r] 这一部分)的状态只能为 0 或 1(即长度为 1 的 01 串)。实际上,这样的操作相当于不断往一个串中添 0 或 1。

接着,我们可以发现,长度为 lenlenlen 的串,最终一定会被操作成长度为 (len−1) mod (k−1)+1(len - 1)\bmod (k - 1) + 1(len1)mod(k1)+1 的串。也就是说,只要最初的 01 串的长度是确定的,那么最终的 01 串长度同样是确定的。

所以,如果一段区间的长度 lenlenlen 满足 (len−1) mod (k−1)=0(len - 1)\bmod (k - 1) = 0(len1)mod(k1)=0,那么经过若干次操作后,这个区间一定可以变成长度为 1 的 01 串。

因此,枚举 midmidmid 时,我们不需要一个个位置地枚举,只需要满足 (r−mid−1) mod (k−1)=0(r - mid - 1) \bmod (k - 1) = 0(rmid1)mod(k1)=0 即可。

(所以,DP 状态中的 SSS 实际上可能带有若干个前导 0,取决于最终变成的 01 串的长度)


综合上述性质,我们就有了时间复杂度是 O(n2⌊nk⌋2k−1)O(n^2\lfloor\frac{n}{k}\rfloor 2^{k - 1})O(n2kn2k1) 的做法,不过这只是一个上界,并且区间 DP 本身就是跑不满的,所以这个复杂度是可以通过的。

但是,上述做法就不能在转移的过程中计算答案了。

不过,结合前面的这句 “实际上,这样的操作相当于不断往一个串中添 0 或 1”,我们可以发现,当 (len−1) mod (k−1)=0(len - 1) \bmod (k-1) = 0(len1)mod(k1)=0 时,01 串中正好添加了 kkk 个字符。此时,我们就可以把它们再合并一次,同时计算答案了。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cassert>
#include<cstring>

#define MAXN 305
#define MAXM 8

using LL = long long;
using namespace std;

int n, k, a[MAXN], c[1 << MAXM];
LL ans, w[1 << MAXM], f[MAXN][MAXN][1 << MAXM];
const LL INF = 0x3F3F3F3F3F3F3F3F;

int main()
{
    #ifdef FILE
        freopen("Input.in", "r", stdin);
        freopen("Output.out", "w", stdout);
    #endif
    scanf("%d%d", &n, &k);
    for(register int i = 1; i <= n; i++) scanf("%d", a + i);
    for(register int i = 0; i < (1 << k); i++) scanf("%d%lld", c + i, w + i);
    for(register int i = 1; i <= n; i++) for(register int j = 1; j <= n; j++) for(register int S = 0; S < (1 << k); S++) f[i][j][S] = -INF;
    for(register int i = 1; i <= n; i++) f[i][i][a[i]] = 0;
    for(register int len = 2; len <= n; len++)
    {
        for(register int l = 1, r = l + len - 1; r <= n; ++l, ++r)
        {
            int t = (len - 1) % (k - 1);
            if(t == 0) t = k - 1;
            for(register int mid = r - 1; mid >= l; mid -= k - 1)
            {
                for(register int S = 0; S < (1 << t); S++)
                {
                    f[l][r][S << 1] = max(f[l][r][S << 1], f[l][mid][S] + f[mid + 1][r][0]);	//添加一个 0
                    f[l][r][S << 1 | 1] = max(f[l][r][S << 1 | 1], f[l][mid][S] + f[mid + 1][r][1]);	//添加一个 1
                }
            }
            if(t == k - 1)	//再次合并
            {
                LL g[2] = {-1LL, -1LL};
                for(register int S = 0; S < (1 << k); S++) g[c[S]] = max(g[c[S]], f[l][r][S] + w[S]);
                f[l][r][0] = g[0], f[l][r][1] = g[1];
            }
        }
    }
    ans = -INF;
    for(register int i = 0; i < (1 << (k - 1)); i++) ans = max(ans, f[1][n][i]);
    printf("%lld\n", ans);
    return 0;
}

END\mathit{END}END

【博士论文复现】【阻抗建模、验证扫频法】光伏并网逆变器扫频与稳定性分析(包含锁相环电流环)(Simulink仿真实现)内容概要:本文档是一份关于“光伏并网逆变器扫频与稳定性分析”的Simulink仿真实现资源,重点复现博士论文中的阻抗建模与扫频法验证过程,涵盖锁相环和电流环等关键控制环节。通过构建详细的逆变器模型,采用小信号扰动方法进行频域扫描,获取系统输出阻抗特性,并结合奈奎斯特稳定判据分析并网系统的稳定性,帮助深入理解光伏发电系统在弱电网条件下的动态行为与失稳机理。; 适合人群:具备电力电子、自动控制理论基础,熟悉Simulink仿真环境,从事新能源发电、微电网或电力系统稳定性研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①掌握光伏并网逆变器的阻抗建模方法;②学习基于扫频法的系统稳定性分析流程;③复现高水平学术论文中的关键技术环节,支撑科研项目或学位论文工作;④为实际工程中并网逆变器的稳定性问题提供仿真分析手段。; 阅读建议:建议读者结合相关理论教材与原始论文,逐步运行并调试提供的Simulink模型,重点关注锁相环与电流控制器参数对系统阻抗特性的影响,通过改变电网强度等条件观察系统稳定性变化,深化对阻抗分析法的理解与应用能力。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值