http://poj.org/problemstatus?problem_id=1159

这个题目即给定一个字符串,让你计算至少插入几个字母,就可将原字符串改为回文序列。

设该字符串一共有n个字母,为str[1:n]

状态dp[i][j]定义为:对于子串str[i:j]构成回文数需要插入字母数量的最小值。

那么可知当头尾字母相同时,即str[i]=str[j]时,dp[i][j]=dp[i+1][j-1]

如果头尾字母不想同时,那么可以在头部加一个和末尾一样的字母构成回文序列dp[i][j]=dp[i+1][j]+1

                                 也可以在尾部加一个和头部一样的字母构成回文序列dp[i][j]=dp[i][j-1]+1

状态转移方程得出来后代码的实现方式可以有两种方法,记忆搜索和自底向上的循环递推实现。

但是本题肯定不能用记忆搜索,空间会超出要求,因为n可以达到5000 那么子递归树即使记忆化后去除重复也得有5000*5000/2这么大的数量,必定会超内存。

用自底向上的递归如果直接开int dp[5000][5000];既然也会超内存,不过后来改成short之后,险过。。。

不过最好的方法当然是滚动数组啦。。。

还有dp[][]自底向上展开的时候注意顺序

#include<iostream>
using namespace std;
const int SIZE=5001;
short dp[SIZE][SIZE];
char str[SIZE];
int min(int a,int b){
if(a<b)return a;
else return b;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>str[i];
dp[i][i]=0;
}
for(int r=2;r<=n;r++)
for(int p=1;p<=n+1-r;p++){
int i=p,j=r+p-1;
if(str[i]==str[j])
dp[i][j]=dp[i+1][j-1];
else
dp[i][j]=min(dp[i+1][j]+1,dp[i][j-1]+1);
}
cout<<dp[1][n]<<endl;
return 0;
}

http://poj.org/problem?id=3991

此题类似,不过由于时间复杂度达到O(n*n*n)所以超时了。。。不过已经是动态规划了啊,怎么缩小计算量呢???求解。。。

#include<iostream>
using namespace std;
const int SIZE=2001;
short dp[SIZE][SIZE];
char str[SIZE];
int min(int a,int b){
if(a<b)return a;
else return b;
}
int main(){
int t=1;
while(cin>>str&&str[0]!='-'){
int n=strlen(str);
/**/for(int i=1;i<=n;i++)
dp[i][i]=0;
for(int r=2;r<=n;r++)
for(int p=1;p<=n+1-r;p++){
// 坐标转换
int i=p,j=r+p-1;
//通过处理两头来递归构造(先处理边界)
if(str[i-1]=='{'&&str[j-1]=='}')
dp[i][j]=dp[i+1][j-1];
else if(str[i-1]=='{'&&str[j-1]=='{')
dp[i][j]=dp[i+1][j-1]+1;
else if(str[i-1]=='}'&&str[j-1]=='}')
dp[i][j]=dp[i+1][j-1]+1;
else
dp[i][j]=dp[i+1][j-1]+2;

//通过中间分开求最优解
for(int k=i+1;k<=j-2;k++)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
}
cout<<t++<<". "<<dp[1][n]<<endl;
}

return 0;
}

PS:这道题目,昨晚躺床上想了好久,感觉,如果用DP方法的话,貌似逃不了N立方的复杂度。但是如果用贪心的话,居然可以O(n)的时间复杂度,看来DP也不是最牛逼的,贪心果然很给力。。。

贪心策略:

设置一个虚拟的栈stack,实际上只为一个整形数据,用来记录栈里面的元素个数,从左到右以次检验字符串的每一个元素,如果是“{”stack加一,表示进栈。如果是“}”的话,先检查stack是否大于零,如果大于零,说明栈里面有左括号,那么拿最上面的那个和他配对即可,stack减一。如果stack为零,说明此时没有左括号,那么翻转一次(flipNum加一),然后把它作为左括号加进栈里(stack加一)。最后,flipNum加上stack/2(道理很显然。。。)代码如下:

int main(){
int t=1;
int stack,flipNum;
while(cin>>str&&str[0]!='-'){
int n=strlen(str);
stack=flipNum=0;
for(int i=0;i<n;i++){
if(str[i]=='}'){
if(stack>0)stack--;
else {
flipNum++;
stack++;
}
}
else{
stack++;
}
}
flipNum+=stack/2;
cout<<t++<<". "<<flipNum<<endl;
}
return 0;
}




posted on 2011-09-30 02:37  geeker  阅读(655)  评论(0编辑  收藏  举报