zoukankan      html  css  js  c++  java
  • CF938F

    (镇楼图,为 CF 评测量提供了一份力量/qiang)

    这题真的把我心态搞崩溃了……

    先是一波三折的心路历程,然后写了个假的 WA 了,看了半天题解。

    去世贸打了场模拟赛回来写了正解,然后又 WA 了,输出了一堆问号。跟 tzc 对拍了半天没拍出错,最终还是在 yxh 的帮助下才 A 掉。stO yxh Orz


    Portal

    考虑状压 DP。

    我们考虑 (dp_{i,j}) 表示到考虑到第 (i) 个字符,第 (i) 个字符必须选,当前还剩 bitmask (j) 里的长度没被删除时所能剩下的字典序最小的字符串。那么不难发现,一个 (dp) 元素的空间是 (mathrm O(n)) 的。然后转移的话,朴素的就考虑枚举当前第 (i) 个字符与上一个字符的距离,这样就是四方的总复杂度,很显然吃不消。

    有两个地方可以优化,一个是 (dp) 元素的储存空间,存一个字符串的 DP 我还是真没见过;另一个是转移。

    一开始想了个假的:正着不行我们倒着 DP,即考虑后缀。这样子我们只需要转移的时候看最左边字符最小的转移对象即可(?),然后最后从前往后路径还原,刚好符合字典序的贪心。事实上当时我脑子坏掉了,这个想法 sb 到家了。错误点很明显,就是显然有可能有左边字符最小且相同的两个转移对象的存在,这时候我们就要比下一位,如果还是相等就还要比下一位,如此还是要存整个串。况且细想起来,两种都是将当前要求的 DP 值拆成当前字符和去掉当前字符得到的前 / 后缀,因为当前字符固定,所以比前 / 后缀大小,本质上是一样的,本该无法优化。

    正解是这样处理的:考虑到字典序的特性:如果两个转移对象,它们如果不相同,那么当前字符就没有任何比较的意义,因为两个转移对象就可以直接决定它们的大小,不像求和啥的位置靠后的贡献还能稍微贡献点力量,字典序是找到第一个不相同的地方后面就卵用没有了。所以说最终要求最小串,就得在从前往后决策的过程中每一步都要是当前最好的。于是我们 dark 以将 DP 值定义成 bool,表示是否可以剩下当前长度中的最优决策。

    考虑转移,那么就还是依然枚举上一位的位置,其中有 true 是先决条件。因为不仅要前面最小,当前位也要最小。于是我们稍微改变一下 DP 顺序,在剩下串长度上面迭代,找出本层中 true 的最小当前位,然后所有等于最小当前位的就是真正的 true 了。最后路径还原显然是可以的,不过既然我们基于剩下串长度迭代了,那可以更简单地直接按顺序输出当前的最小当前位。至此,时间复杂度三方。

    插一嘴晚上交那么多遍的那个错误:对于有些 (i,j) 虽然可能的确可以剩下当前最优决策,但是它终究是构不成最终完整的串的,因为后面空间不足,这种是不能算的,不然就可能导致到后面整个 DP 数组没有一个达到最优决策的。然后又发现,空间足的也就一定足,这是确定下来的,那么我们可以直接不考虑那些空间不足的。就这说明有些时候 DP 不能只考虑当前考虑到的地方,有些后效性无伤大雅的能简单处理就处理掉了。

    然后考虑如何优化转移。注意到对于每个 (i)(j) 们,都完整地构成 (0sim 2^{lfloorlog_2n floor}-1),于是异常显然地可以枚举子集做到 (mathrm O!left(3^{log_2n}n ight)),不过这样还是过不去。注意到我们每次枚举了距离,而这可能是多个单个 (2) 的幂的混合物,我们枚举这个混合物干嘛呢?DP 想要优化当然是把阶段、状态能拆就拆啦,多拆来实现指数降多项式。于是我们可以将 DP 定义变一下,不一定要当前字符一定选了。于是每次就要么当前位选,要么枚举最后一段被删除的纯净物,这样就只需要枚举纯净物,就把复杂度降下来了。

    复杂度平方对数,常数小跑得飞快。

    (我咋写了这么长啊???)

    #include<bits/stdc++.h>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int N=5000;
    int n,m;
    char a[N+5];
    int dp[N+1][N+1];
    int main(){
    //	freopen("a.in","r",stdin);
    	cin>>a+1;
    	n=strlen(a+1);
    	m=log2(n);
    	for(int i=0;i<1<<m;i++)dp[i][i]=true;
    	for(int i=1;i<=n-(1<<m)+1;i++){
    		int mn=inf;
    		for(int j=i;j<i+(1<<m);j++)if(dp[j-1][j-i])mn=min(mn,int(a[j]));
    		for(int j=i;j<i+(1<<m);j++)dp[j][j-i]=dp[j-1][j-i]&&a[j]==mn;
    		for(int j=i;j<i+(1<<m);j++)
    			for(int k=0;k<m;k++)if(j-i&1<<k)
    				dp[j][j-i]|=dp[j-(1<<k)][j-i^1<<k];
    		putchar(mn);
    	}
    	return 0;
    }
    
  • 相关阅读:
    王重阳160809311第9次作业
    160809311王重阳第8次作业
    160809311王重阳第七次作业
    160809311王重阳作业6
    王重阳160809311第5次作业
    王重阳160809311作业四
    第六次Java作业
    李喆第12次作业
    李喆第11次作业
    李喆第9次作业
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/cf938f.html
Copyright © 2011-2022 走看看