1. 程式人生 > >模板庫(四) - 動態規劃演算法模板

模板庫(四) - 動態規劃演算法模板

寫在前面

模板庫”這一系列文章用來複習 O I OI 模板
由於時間原因,作者無法一一親自除錯其中的程式,也因如此,有一部分程式來自於網際網路,如果您覺得這侵犯了您的合法權益,請聯絡 ( Q

Q 2068926345 ) (QQ2068926345) 刪除。
對於給您造成的不便和困擾,我表示深深的歉意。
本系列文章僅用於學習,禁止任何人或組織用於商業用途。
本系列文章中,標記*的為選學演算法,在 N O
I P NOIP
中較少涉及。

動態規劃

簡單線性動態規劃

LIS(最長上升子序列)

【簡介】

最長上升子序列( L o n g

e s t Longest I n c r e a s i n g Increasing S u b s e q u e n c e Subsequence , L I S LIS ),在電腦科學上是指一個序列中最長的單調遞增的子序列。

【程式碼實現】
樸素方法
#include<cstdio>
#include<iostream>
using namespace std;
int n,a[5001],f[5001]={0,1},ans;
int main(){
    scanf("%d",&n); 
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),f[i]=1;
    for(int i=2;i<=n;i++){
        for(int j=1;j<i;j++)
            if(a[j]<a[i])f[i]=max(f[i],f[j]+1);
        ans=max(ans,f[i]);
    }
    printf("%d",ans);
    return 0; 
} 

複雜度 Θ ( n 2 ) Θ(n^2)

貪心實現
#include<cstdio>
#include<algorithm>
using namespace std;
int n,f[1000001],len,x; 
int main(){
    scanf("%d%d",&n,&x);
    f[++len]=x;
    for(int i=2;i<=n;i++){
        scanf("%d",&x);
        if(x>f[len]) f[++len]=x;
        else f[lower_bound(f+1,f+len+1,x)-f]=x;
    }
    printf("%d",len);
    return 0;
}

複雜度 Θ ( n Θ(n l o g n ) logn)

LCS(最長公共子序列)

【簡介】

L C S LCS L o n g e s t Longest C o m m o n Common S u b s e q u e n c e Subsequence 的縮寫,即最長公共子序列。一個序列,如果是兩個或多個已知序列的子序列,且是所有子序列中最長的,則為最長公共子序列。

【程式碼實現】
樸素做法
#include<cstdio>
#include<algorithm>
using namespace std;
int n,a[1007],b[1007],dp[1007][1007];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) scanf("%d",&b[i]);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
        if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1);
        else f[i][j]=max(f[i-1][j],f[i][j-1]);
    printf("%d",f[n][n]);
    return 0;
}

複雜度 Θ ( n 2 ) Θ(n^2)

利用 L I S LIS 優化
#include<iostream>
#include<ctype.h>
#include<cstdio>
#include<algorithm>
using namespace std;
inline int read(){
	int x=0,f=0;char ch=getchar();
	while(!isdigit(ch))f|=ch=='-',ch=getchar();
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return f?-x:x;
}
int a,b[100007],Transformation[1000007],f[1000007];
int main(){
	int n=read();
	for(int i=1;i<=n;++i)a=read(),Transformation[a]=i;
	for(int i=1;i<=n;++i)b[i]=read(),b[i]=Transformation[b[i]];
	int o=1;
	f[o]=b[o];
	for(int i=2;i<=n;++i){
		if(b[i]>f[o])f[++o]=b[i];
		else f[upper_bound(f+1,f+o+1,b[i])-f]=b[i];
	}
	cout<<o;
	return 0;
}

複雜度 Θ ( n Θ(n l o g n ) logn)

LCIS(最長公共上升子序列)

【簡介】

給定兩個整數序列,求它們的最長上升公共子序列。

//Cogs 1669神祕的咒語
#include<iostream>
#include<cstdio>
#include<ctype.h>
#include<cstring>
using namespace std;
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(!isdigit(ch))f|=ch=='-',ch=getchar();
    while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    return f?-x:x;
}
int a[507],b[507],f[507];
int main(){
	freopen("codes.in","r",stdin);
	freopen("codes.out","w",stdout);
    int T=read();
    while(T--){
    	memset(f,0,sizeof f);
		int n=read(),ans=0;
		for(int i=1;i<=n;++i)a[i]=read();
		int m=read();
		for(int i=1;i<=m;++i)b[i]=read();
		for(int i=1;i<=n;++i){
			int Max=0;
			for(int j=1;j<=m;++j){
				if(a[i]>b[j])Max=max(Max,f[j]);
				if(a[i]==b[j])f[j]=Max+1;
			}
		}
		for(int i=1;i<=m;++i)ans=max(ans,f[i]);
	    printf("%d\n",ans);
	}
	fclose(stdin);fclose(stdout);
    return 0;
}

複雜度 Θ ( n 2 ) Θ(n^2)

最大子段和問題

【簡介】

給定 n n 個整數(可能為負數)組成的序列 a 1 , a 2 , a 3 , , a n a_1,a_2,a_3,…,a_n 求該序列如 a i + a i + 1 + + a j a_i+a_{i+1}+…+a_j 的子段和的最大值。當所給的整數均為負數時定義子段和為 0 0 ,依此定義,所求的最優值為:
M a x { 0 , a i + a i + 1 + + a j } , 1 &lt; = i &lt; = j &lt;