最长公共子序列:
对A序列和B序列
(1) i=0,j=0 dp[i][j]=0;
(2) X[I]==Y[j]dp[i][j]=dp[i-1][j-1]+1;
X={A B C B } Y={B D C AB} Z={B C }->Z={B C B }
(3) X[I] != Y[j] dp[i][j]=max{dp[i][j-1],dp[i-1][j-1]}
X={A B C B D} Y={B D C AB}
Z1={B C B} (去掉Xi(这个例子是D)之后,也就是Xi-1和Yj 的最长公共字序列)
Z2={B C} (去掉Yj(这个例子是B)之后,也就是Xi和Yj-1 的最长公共字序列)
由于每次的动态规划都是只和上一次有关,所以也可以用滚动数组记录。
比如下面的poj1159
POJ1458
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <iostream>
#define maxn 1005
using namespace std;
char s[maxn],ss[maxn];
int dp[maxn][maxn];
int main(){
while(scanf("%s%s",s,ss)!=EOF){
int len1=strlen(s);
int len2=strlen(ss);
memset(dp,0,sizeof(dp));
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(s[i-1]==ss[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
}
else{
dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
}
}
}
//注意i,j的初始值和意义
//如果是i=0,j=0开始
/*
for(int i=0;i<len1;i++){
for(int j=0;j<len2;j++){
if(s[i]==ss[j]){
dp[i+1][j+1]=dp[i][j]+1;
}
else{
dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
}
}
}
*/
printf("%d\n",dp[len1][len2]);
}
}
poj2250
和模板差不多的,就是从比较字母变成了比较单词。处理一下输入就好了
设置一个二维数组存
另外这个是一个记录路径的题。
用map[i][j]=1,2,3分别表示dp[i][j]的3个来源:dp[i-1][j-1];dp[i-1][j];dp[i][j-1];
然而样例能过,官网WA……TT也不知道哪里错了
还请看到的大神指点迷津
先把代码发到这里…发现错误是啥了我再来改TT
小伙伴们帮帮忙TT
#include <cstdio>
#include <iostream>
#include <stack>
using namespace std;
stack<string> sta;
char s[105][35],ss[105][35];
int dp[105][105];
int map[105][105],len1,len2;
//记录dp[i][j]来源路径
//1. dp[i-1][j-1],
//2. dp[i-1][j],
//3. dp[i][j-1]
void LCS(){
memset(dp,0,sizeof(dp));
memset(map,0,sizeof(dp));
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(strcmp(s[i-1],ss[j-1])==0){
dp[i][j]=dp[i-1][j-1]+1;
map[i][j]=1;
}
else if(dp[i-1][j]>dp[i][j-1]){
dp[i][j]=dp[i-1][j];
map[i][j]=2;
}
else {
dp[i][j]=dp[i][j-1];
map[i][j]=3;
}
}
}
//测试输出:
/*
for(int i=0;i<=len1;i++){
for(int j=0;j<=len2;j++){
printf("%d ",dp[i][j]);
}
printf("\n");
}
printf("%d~~~~\n",dp[len1][len2]);
for(int i=0;i<=len1;i++){
for(int j=0;j<=len2;j++){
printf("%d ",map[i][j]);
}
printf("\n");
}
*/
}
void Print(){
for(int i=len1;i>=1;){
for(int j=len2;j>=1;){
if(map[i][j]==1){
// printf("%s ",s[i-1]); 这样是正序输出的,所以需要存到栈里面
string str=s[i-1];
sta.push(str);
i--;j--;
}
else if(map[i][j]==2){
i--;
}
else{
j--;
}
}
}
while(!sta.empty()){
printf("%s ",sta.top().c_str());
sta.pop();
}
printf("\n");
}
int main(){
while(scanf("%s",s[0])!=EOF){
while(!sta.empty()){
sta.pop();
}
int k=1;
while(scanf("%s",s[k])!=EOF){
if(s[k][0]=='#')break;
k++;
}
len1=k;
k=0;
while(scanf("%s",ss[k])!=EOF){
if(ss[k][0]=='#')break;
k++;
}
len2=k;
LCS();
Print();
memset(s,'\0',sizeof(s));
memset(ss,'\0',sizeof(ss));
}
return 0;
}
Poj1159
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 58763 | Accepted: 20419 |
Description
As an example, by inserting 2 characters, the string "Ab3bd" can be transformed into a palindrome ("dAb3bAd" or "Adb3bdA"). However, inserting fewer than 2 characters does not produce a palindrome.
Input
Output
Sample Input
5 Ab3bd
Sample Output
2
题意: 输出至少需要往序列中加入多少个字母才能使序列变长回文串
实际上就是n-正序逆序的最长子序列
开始想着直接输入的时候是前一半和后一半字符串的最长子序列,但是输入总有问题…就改成尝试网上的正逆串比较了~
dp[maxn][maxn]
当maxn太大时,会超出内存限制,需要改装成滚动数组【常在dp中使用】因为后面的结果就仅仅依赖于前面最近的一次结果。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define maxn 5005
using namespace std;
char S[maxn],T[maxn];
int dp[2][maxn];
int main(){
int n;
// freopen("1159.txt","r",stdin);
scanf("%d",&n);
scanf("%s",S);
for(int i=0;i<n;i++){
T[n-1-i]=S[i];
}
int e=0;
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++){
e=1-e;
for(int j=0;j<n;j++){
if(S[i]==T[j]){
dp[e][j+1]=dp[1-e][j]+1;
}
else{
dp[e][j+1]=max(dp[e][j],dp[1-e][j+1]);
}
}
}
cout<<n-dp[e][n]<<endl;
return 0;
}
超水的本人,读取一半字符串的代码在下面…发现问题的麻烦联系我TT…始终不能正确写入T……
scanf("%d",&n);
int i;
getchar();
for( i=0;i<n/2;i++){
scanf("%c",&S[i]);
}
if(n%2==0){
scanf("%c",&S[++i]);
}
else{getchar();}
//cout<<"i="<<i<<endl;
int j;
for( j=0;i+j<n-1;j++){
scanf("%d",&T[j]);
}