目录
B:POJ-2533 Longest Ordered Subsequence
LIS(最大上升子序列)
思路
状态设计:dp[i]代表以a[i]结尾的LIS的长度
状态转移:dp[i]=max(dp[i], dp[j]+1) (1<=j< i, a[j]< a[i])
边界处理:dp[i]=1 (1<=i<=n)
时间复杂度:O(N^2)
模板
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#define MAXN 1000
using namespace std;
typedef long long ll;
int ans[MAXN+5];
int dp[MAXN+5];
int main()
{
int n,re=1;
cin>>n;
for(int i=1;i<=n;++i){
cin>>ans[i];
}
for(int i=1;i<=n;++i){
dp[i]=1;
for(int j=1;j<=i-1;++j){
if(ans[j]<ans[i]) dp[i]=max(dp[i],dp[j]+1);
}
re=max(re,dp[i]);
}
cout<<re<<endl;
}
LCS(最长公共子序列)
思路(转自LCS)
dp[i][j]表示字符串x的前i个字符构成的子串和字符串y的前j个字符构成的子串的最长公共子序列的长度
状态转移方程
i=0 or j=0说明有一个字符串的长度为0,因此最长公共组序列长度为0。当字符串x的第i个字符和字符串y的第j个字符相等时,dp[i][j]就等于dp[i-1][j-1]+1,如果不等dp[i][j]=max(dp[i][j-1],dp[i-1][j])
模板
int dp[1000][1000]
int Lcs(string x,string y)
{
for(int i=0;i<=x.length();i++)
for(int j=0;j<=y.length();j++)
if(i==0||j==0)
dp[i][j]=0;
else if(x[i-1]==y[j-1])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
return dp[x.length()][y.length()];
}
例题
A:POJ-1836 Alignment:LIS变形题,看懂题意应该就能写出来,看不懂的可以看Alignment题解,AC代码:
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn=1e4+7;
const int INF=0x3f3f3f3f;
double hige[maxn];
double size1[maxn],dp[maxn],dp1[maxn];
int main()
{
int n;
while(cin>>n){
for(int i=1;i<=n;i++){
cin>>hige[i];
}
for(int i=1;i<=n;i++){
dp[i]=1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<i;j++){
if(hige[j]<hige[i])
dp[i]=max(dp[i],dp[j]+1);
}
}
for(int i=1;i<=n;i++){
dp1[i]=1;
}
for(int i=n;i>=1;i--){
for(int j=i+1;j<=n;j++){
if(hige[j]<hige[i])
dp1[i]=max(dp1[i],dp1[j]+1);
}
}
int ans=0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(dp[i]+dp1[j]>ans)
ans=dp[i]+dp1[j];
cout<<n-ans<<endl;
}
return 0;
}
B:POJ-2533 Longest Ordered Subsequence:本题考查LIS的O(nlogn)算法,具体解析可看(最长上升子序列(LIS)长度的O(nlogn)算法),AC代码:
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn=1e3+7;
const int INF=0x3f3f3f3f;
int a[maxn],s[maxn],tmp;
int main()
{
int n;
while(cin>>n){
for(int i=1;i<=n;i++)
cin>>a[i];
int tot=1;
s[tot]=a[1];
for(int i=2;i<=n;i++){
if(a[i]>s[tot])
s[++tot]=a[i];
else{
tmp=lower_bound(s+1,s+tot+1,a[i])-s;
s[tmp]=a[i];
}
}
cout<<tot<<endl;
}
return 0;
}
C:HDU-1159 Common Subsequence:LCS最长公共子序列裸体,AC代码:
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn=1e3+7;
const int INF=0x3f3f3f3f;
char str1[maxn],str2[maxn];
int dp[maxn][maxn];
int main()
{
while(cin>>str1>>str2){
int n=strlen(str1),m=strlen(str2);
memset(dp,0,sizeof(dp));
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++){
if(i==0||j==0)
dp[i][j]=0;
else if(str1[i-1]==str2[j-1])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
cout<<dp[n][m]<<endl;
}
return 0;
}
D:POJ-2250 Compromise:LCS的记录路径问题的模板题。看下代码应该就能明白:
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn=1e2+7;
const int INF=0x3f3f3f3f;
char str1[maxn][50],str2[maxn][50];
int dp[maxn][maxn];
void dfs(int i, int j)
{
if (i == 0 || j == 0)
return;
if (strcmp(str1[i-1],str2[j-1])==0){
dfs(i - 1, j - 1);
cout <<str1[i-1]<< " ";
}
else
{
if (dp[i - 1][j] > dp[i][j - 1])
dfs(i - 1, j);
else
dfs(i, j - 1);
}
}
int main()
{
char s[50];
while(cin>>s){
int n=0,m=0;
while(strcmp(s,"#")!=0){
strcpy(str1[n++],s);
cin>>s;
}
cin>>s;
while(strcmp(s,"#")!=0){
strcpy(str2[m++],s);
cin>>s;
}
memset(dp,0,sizeof(dp));
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++){
if(i==0||j==0)
dp[i][j]=0;
else if(strcmp(str1[i-1],str2[j-1])==0){
dp[i][j]=dp[i-1][j-1]+1;
}
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
dfs(n,m);
cout<<endl;
}
return 0;
}
E:POJ-2264 Advanced Fruits:LCS记录路径 + 最短公共父序列。和上一题的区别是要把各自独特含有的输出出来,还是直接上代码,代码挺好理解的:
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn=1e2+7;
const int INF=0x3f3f3f3f;
char str1[maxn],str2[maxn];
int dp[maxn][maxn],ans[maxn][maxn];
void dfs(int i, int j)
{
if(i==0&&j==0)//注意是且,因为要把各自独有的都输出完
return ;
if(ans[i][j]==0){
dfs(i-1,j-1);
cout <<str1[i-1];
}
else if (ans[i][j]==1){
dfs(i-1, j);
cout<<str1[i-1];
}
else{
dfs(i,j-1);
cout<<str2[j-1];
}
}
int main()
{
while(cin>>str1>>str2){
int n=strlen(str1),m=strlen(str2);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++) //本题出现ans数组而上题不用的原因就在这和下面的四行的初始化,因为要输出各自独有的内容。
ans[i][0]=1;
for(int i=1;i<=m;i++)
ans[0][i]=-1;
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++){
if(i==0||j==0)
dp[i][j]=0;
else if(str1[i-1]==str2[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
ans[i][j]=0;
}
else if(dp[i-1][j] >= dp[i][j-1]){
dp[i][j] = dp[i-1][j];
ans[i][j]=1;
}
else{
dp[i][j] = dp[i][j-1];
ans[i][j]=-1;
}
}
dfs(n,m);
cout<<endl;
}
return 0;
}
F:HDU-1160 FatMouse's Speed:LIS+结构体,因为排序的条件是两种,可构建结构体,对其中一个条件进行排序,因为要排序,而结果要输出原本的位置,所以结构体还要储存位置,剩下的就和LIS一样了,不过需要输出路径,所以这里的c[i] = j;意思就是当前这个最优解i是由上一个最优解j递推出来的。所以可以用这种方法求的最优路径。AC代码:
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn1=1e3+7;
const int INF=0x3f3f3f3f;
int dp[maxn1],c[maxn1];
struct mouse{
int s,e;
int index;
}p[maxn1];
bool cmp(mouse x,mouse y){
return x.s<y.s;
}
void pri(int k){
if(k==0)
return ;
pri(c[k]);
cout<<p[k].index<<endl;
}
int main()
{
memset(c,0,sizeof(c));
int n=1;
while(scanf("%d%d",&p[n].s,&p[n].e)!=EOF){
p[n].index=n;
++n;
}
sort(p+1,p+n+1,cmp);
for(int i=0;i<=n;i++)
dp[i]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
if(p[j].e>p[i].e&&p[i].s!=p[j].s&&(dp[j]+1>dp[i])){
dp[i]=dp[j]+1;
c[i]=j;
}
int maxn=0,maxm=0;
for (int i=1;i<=n;i++)
if (maxn<dp[i]){
maxn=dp[i];
maxm=i;
}
cout<<maxn<<endl;
pri(maxm);
return 0;
}