• P3847 [TJOI2007]调整队形 题解


    博客园同步

    原题链接

    简要题意:

    每次可以在数组中插入一个数(可以在两端)或删除一个数,改变一个数;求让数组成为回文数组的最小步数。

    这题的蓝太假了,水 ( ext{dp}),想到算法就可以做出。

    关键还是要想到用 动态规划 啊,要是想不到这题就做不出来了。

    (考场上可以先用搜索,然后记忆化推导也行,但是这里就不推了)

    显然考虑区间 ( ext{dp}).

    (f_{i,j}) 表示将 (a_i)(a_j) 变成回文的最小步数。

    那么有:

    [ f_{i,j} = egin {cases} f_{i+1,j-1} , s_i = s_j \ min(f_{i+1,j} , f_{i,j-1} , f_{i+1,j-1}) , s_i ot = s_j \ end{cases} ]

    下面一一解释。

    如果 (s_i = s_j),则说明已经回文了一位,直接考虑砍掉它们的答案。

    (因为回文串两边添上或去掉一个相同的字符,仍然具有回文性)

    否则,(f_{i+1,j}) 是去掉第 (i) 位的方案,(f_{i,j-1}) 是去掉第 (j) 位的方案。

    (f_{i+1,j-1}) 是改变 第 (i) 位或第 (j) 位使得 (s_i = s_j),然后砍掉它们的答案。

    均需要一步,所以最后 (+1).

    你会问:添加操作没有用?

    你想,假设你在某个位置添加一个数,等价于在其对应的位置去掉一个数。

    这仍然是因为上述的回文性质。在对应的位置删除或添加一个相同的数,不影响回文性质。

    时间复杂度:(O(n^2)).

    实际得分:(100pts).

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=3e3+1;
    
    inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
    	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
    
    int n,a[N];
    int f[N][N];
    
    int main(){
    	n=read();
    	for(int i=1;i<=n;i++) a[i]=read();
    	for(int len=1;len<=n;len++) //区间 dp 的套路,为了满足无后效性,需要按照区间大小进行枚举,而不是按照左右端点
    	for(int i=1;i<=n-len+1;i++) {
    		int j=i+len-1;
    	// 如果 len=1 则 f[i][j]=f[i+1][j-1]=0 不影响答案,不必特判
    		if(a[i]==a[j]) f[i][j]=f[i+1][j-1];
    		else f[i][j]=min(f[i+1][j],min(f[i][j-1],f[i+1][j-1]))+1;
    	} printf("%d
    ",f[1][n]); //最终答案
    	return 0;
    }
    
    
  • 相关阅读:
    NOI 2016 区间 解题报告
    有关莫队
    [JSOI2008]最大数 线段树解法
    HDU P3341 Lost's revenge 题解+数据生成器
    BZOJ P1212 [HNOI2004] L语言
    洛谷P3168 [CQOI2015]任务查询系统
    普通平衡树Tyvj1728、luogu P3369 (splay)
    洛谷P3384 树链剖分
    BZOJ P2157 旅游
    【算法导论】第6章,堆排序
  • 原文地址:https://www.cnblogs.com/bifanwen/p/12607880.html
Copyright © 2020-2023  润新知