zoukankan      html  css  js  c++  java
  • POJ 3280 Cheapest Palindrome 区间DP

    POJ 3280 Cheapest Palindrome 区间DP

    题意

    给你一个长度为m的字符串,然后给你n种操作,操作都是一些对某个字母进行增加和减少时所需要的花费,我们需要用这些若干个操作来使得给的字符串变成一个回文字符串,问最小花费是多少?

    解题思路

    实话说,我第一次看到这个题想到了针对于回文串的Manacher算法,但是这个最优问题没有办法搞定啊,于是停摆了一段时间,知道上网查了查(还是菜,ε=ε=ε=┏(゜ロ゜;)┛),说是区间dp的入门题,害,我之前做的那些区间dp题的知识都还给学长了。废话不多说,下面开始进入正题。

    首先说一下为什么是区间dp,因为我们需要一段一段区间地进行求解,通过合并小区间的最优解来得出整个大区间的最优解的dp算法。

    首先我们定义dp[i][j]为使得区间i到区间j成为回文串的最小代价,那么就有下们这几种情况了:

    1. dp[i+1][j]表示区间i到区间j已经是回文串了的最小代价,那么对于s[i]这个字母,我们有两种操作,删除与添加,对应有两种代价,dp[i+1][j]+add[ss[i]-'a'], dp[i+1][j]+del[ss[i]-'a'],取这两种代价的最小值;

    2. dp[i][j-1]表示区间i到区间j-1已经是回文串了的最小代价,那么对于ss[j]这个字母,同样有两种操作,dp[i][j-1]+add[ss[j]-'a'],dp[i][j-1]+del[ss[j]-'a'],取最小值;

    3. 若是ss[i]==ss[j],dp[i+1][j-1]表示区间i+1到区间j-1已经是回文串的最小代价,那么对于这种情况,我们考虑dp[i][j]dp[i+1][j-1]的大小;

    然后dp[i][j]取上面这些情况的最小值,其实我们可以提前对每一个字符是增加还是删除的操作做一个比较,我们存下最小值就行了。

    代码实现

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<string>
    #include<stack>
    #include<queue>
    #include<map>
    #include<set>
    #include<sstream>
    typedef long long ll;
    using namespace std;
    const double esp=1e-6;
    const int inf=0x3f3f3f3f;
    const int MAXN=2e3+7;
    int n, m;
    char ss[MAXN];
    int dp[MAXN][MAXN];
    int cost[26];
    int main()
    {
    	cin>>n>>m;
    	cin>>ss;
    	char tmp[2];
    	int a, b;
    	for(int i=1; i<=n; i++)
    	{
    		cin>>tmp>>a>>b;
    		cost[tmp[0]-'a'] = min(a, b);
    	}
    	int len = strlen(ss);
    	for(int k = 1; k<=len; k++)//区间的长度
    		for(int i=0; i+k-1 < len; i++)//i为左端点, j为右端点
    		{
    			int j = i + k - 1;
    			if(i != j)
    				dp[i][j] = inf;
               	else {
    				if(ss[i] == ss[j])
    					dp[i][j] = dp[i+1][j-1];
    				else{
    					dp[i][j] = min(dp[i+1][j]+cost[ss[i]-'a'], dp[i][j]);
    					dp[i][j] = min(dp[i][j-1]+cost[ss[j]-'a'], dp[i][j]);	
    				}
                }
    		}
    	cout<<dp[0][len-1]<<endl;
    	return 0;
    }
    

    看了一下网上的代码,还有另一种实现区间dp的方式,第一次看到,也加上了。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<string>
    using namespace std;
    int n,m,dp[2009][2009],in[27],de[27];
    char ch[2009];
    int main()
    {
    	scanf("%d%d",&n,&m);
    	scanf("%s",ch);
    	for (int i=1;i<=n;i++) 
    	{
    		char c;
    		if (scanf("%c",&c)&&c=='
    ') scanf("%c",&c);
    		int k1,k2;
    		scanf("%d%d",&k1,&k2);
    		in[c-'a']=k1;de[c-'a']=k2;
    	}
    	for (int i=m-1;i>=0;i--)
    	{
    		dp[i][i]=0;
    		for (int j=i+1;j<m;j++)
    		{
    			dp[i][j]=0x3f3f3f3f;//因为找最小的,别忘了开始时置为无穷大
    			if (ch[i]==ch[j]) dp[i][j]=dp[i+1][j-1];
    			dp[i][j]=min(dp[i][j],min(dp[i+1][j]+in[ch[i]-'a'],dp[i+1][j]+de[ch[i]-'a']));
    			dp[i][j]=min(dp[i][j],min(dp[i][j-1]+in[ch[j]-'a'],dp[i][j-1]+de[ch[j]-'a']));//三种情况
    			//printf("%d %d %d
    ",i,j,dp[i][j]);
    		}
    	}
    	printf("%d",dp[0][m-1]);
    	return 0;
    }
    
  • 相关阅读:
    Java知识积累3-XML的DOM解析修改和删除方法
    Java知识积累-XML的DOM解析修改和删除方法
    Java知识积累2-StringReverse实现文字(单词)倒叙输出
    Java知识积累1-StringAlign实现文字居中左右对齐
    String中具有的方法
    有17个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下 的是原来第几号的那位
    编写一个程序,求出整数数组中最小元素的下标。如果这样的元素个数大于1,则返回下标最小的数的下标。
    初识
    关于点击跳转页面
    sql存储过程
  • 原文地址:https://www.cnblogs.com/alking1001/p/12559977.html
Copyright © 2011-2022 走看看