【经典dp】n数填m空,括号序列取第k项,最长非降序列计数及枚举,最短编辑距离

1 m个数填充n个space,每个数至少出现一次。

代码 1

#include <iostream>
using namespace std;
typedef long long int ll;
ll a[101][101];
#define MOD 1000000007
int dp(int n,int m){
	if(n<m||m==0) return 0;
	a[0][0]=1;
	for(int i=1;i<=n;i++){//n spaces
		for(int j=1;j<=i;j++){
			a[i][j]=a[i-1][j-1]*(m+1-j);
			if(j!=i) a[i][j]+=a[i-1][j]*j;
			a[i][j]%=MOD;
		}
	}
	return a[n][m];
}
int main(){
	int n;cin>>n;
	for(int i=1;i<=n;i++){
		int n,m;cin>>m>>n;
		cout<<"Case #"<<i<<": "<<dp(n,m)<<endl;
	}
	return 0;
}


求n-楼梯数列的第k项,用括号序列表示:

楼梯序列的性质:任意一点左边的上楼梯次数>=下楼梯次数(或左括号个数>=右括号个数). 且2*n长的括号序列个数=C(2*n,n)-C(2*n,n-1)   

代码 2

#include <iostream>
#include <string.h>
using namespace std;
typedef long long int ll;
ll a[201][201];
ll getNum(int k,int n){
    if(k<0||k>n) return 0;  
    int maxN=(n-k)/2; // max value of cnt('(') - cnt(')'): k+x <= n-x, x=(n-k)/2;
    //ll a[n+1][maxN+1];   
    for(int i=0;i<=maxN;i++) a[0][i]=0;
    a[0][k]=1;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=maxN;j++){
            a[i][j]=0;
            if(j<maxN) a[i][j]+=a[i-1][j+1];// (
            if(j>0) a[i][j]+=a[i-1][j-1]; // )
            ///a[i][j]%=MOD; cannot %mod
        }
    }
    return a[n][0];
}
string getKStr(int n,ll k){
    int lst=0; //当前左括号-右括号的值
    ll number=k;
    char s[201];
    for(int i=0;i<2*n;i++){
        s[i]='(';
        ll cnt=getNum(lst+1, 2*n-1-i);
        if(number<=cnt) {
            lst=lst+1; 
        }else{
            number-=cnt;
            s[i]=')';
            cnt=getNum(lst-1,2*n-1-i);
            if(number<cnt) return ""; // no solution
            lst=lst-1;
        }
    }
    s[2*n]=0;
    return string(s);
}

int main(){
    int n;
    for(int i=0;i<n;i++){
        ll N,k;cin>>N>>k;
        cout<<"Case #"<<i+1<<": "<<getKStr(N,k)<<endl;
    }
    return 0;
}


非降序列计数:

状态:a[len]={ <lst, cnt>},长度为len的序列,其中最后一项(也是最大的)值为lst的个数=cnt

when num[i]=x come, 

   for len:= i to 0:

      for j:=minn to maxn:

           if x>=j:     a[len][j] => a[len+1][x].

二维状态 a[len][lst] = cnt

代码 3

#include <iostream>
#include <vector>
#include <map>
using namespace std;
#define MAXN 1000
#define MINN -9999999
typedef pair<int,int> Pair;
#define rep(i,n) for(int i=0;i<(int)(n);i++)
int dp( int num[], int n ){
    vector<Pair> a[MAXN];
    rep( i, n+1 ) a[i].clear();
    a[0].push_back( Pair( MINN, 1 ) ); 
    rep( i, n ){
        int x=num[i];
        for( int m = i ; m >= 0; m-- ){
            int cnt=0;
            rep( j, a[m].size() ){
                Pair &p = a[m][j];
                if( p.first <= x ){
                    cnt += p.second;
                }
                else break;
            }
            if( cnt == 0 ) continue;
            int idx = 0;
            bool tag = false;
            rep( j, a[m+1].size() ){
                Pair &p = a[m+1][j];//@error: a[m][j] mistaked and array overflow
                if( p.first == x ) {
                    p.second += cnt;
                    tag = true;
                    break;
                }
                else if(p.first > x ){
                    a[m+1].insert( a[m+1].begin() + j, Pair( x, cnt ) ); 
                    tag = true;
                    break;
                }
            }
            if( !tag ) a[m+1].insert( a[m+1].end(), Pair( x, cnt ) );
        }
    }
    int cnt = 0;
    int i = n;
    while( i >= 0 && a[i].empty() ) i -- ;
    if( i>=0 )
        rep( j, a[i].size() )
            cnt += a[i][j].second;
    cout<<"longest array:"<<i<<endl;
    return cnt;
}



int main(){
    int num[10] = { 2, 1, 3, 0, -1, 2, 3, 1 };
    cout<<dp( num, 8 )<<endl;
    while( true ){
        int n; cin>>n;
        if( !n ) break;
        for(int i = 0; i < n; i ++ ) cin>>num[i]; 
        cout<<dp( num, n )<<endl;
    }
    return 0;
}


找最长递增序列长度:

关键是 iterator it=lower_bound(a.begin(),a.end(),x),it指向第一个大于等于x的元素。(bigger_bound指向最后一个大于等于x。。)

代码 4

#include <iostream>
#include <vector>
#include <map>
using namespace std;
#define MAXN 1000
#define MINN -9999999
typedef pair<int,int> Pair;
#define rep(i,n) for(int i=0;i<(int)(n);i++)

int dp(int num[], int n){
    vector<int> a;
    typedef vector<int>::iterator IT;
    int len = 0;
    rep( i, n ){
        int x = num[i];
        IT idx = lower_bound( a.begin(), a.end(), x );
        if( idx == a.end() ) a.insert( a.end(), x );
        else *idx = x;
    }
    return a.size();
}
int main(){
    int a[10] = {1,3,-2,5,-4,9,1,-1,10};
    cout<<dp(a,9)<<endl;
    return 0;
}


最长非降子序列的枚举:找到所有最长递增子序列

首先有a=vector<vector<  pair<int, vector<vector<int>>>  >>,保存的是len长度,所有最后一个值和相应子序列集合。

递推方程:

a[len]={ <lst,{子序列} > }, 

a[len] . append(x) => a[len+1]

其实枚举的是每一个num[i]=x,前面接

代码 5

该代码存在一个逻辑疏忽! 当扫描到 num[i]=x时,没必要枚举所有的len=1~i,a[len] =》 a[len+1],

只需要找到第一个a[len],其中包含有小于等于x的pair<int,vector<vector<int>>>就可以了。把该小于等于x的串添加上x,再插入到a[len+1]就完成了。

时间复杂度: n*(log n  + m )= O(n*n)  不是下面代码的O(n^3)

#include <iostream>
#include <algorithm>
using namespace std;
#include <vector>
#include <map>
typedef pair< int, vector<vector<int> > > Pair;

struct cmper{
    bool operator () ( const Pair &a, const Pair &b ){
        return a.first <= b.first;
    }
}cmp;

struct arrsort{
    bool operator () ( const vector<int> &a, const vector<int> &b ){
        for( int i = 0 ; i < a.size() && i < b.size() ; i++ ){
            if( a[i] != b[i] ) return a[i] <= b[i];
        }
        return a.size() <= b.size();
    }
}acmp;

void dp(int num[], int n){
    vector<vector<Pair> > a( n + 1, vector<Pair> () );
    a[0].push_back( Pair( -9999, vector<vector<int> >( 1, vector<int>( 1, -9999 ) ) ) );
    for( int i = 0; i < n; i ++ ){
        int x = num[i];
        for( int j = i; j >= 0; j--){
            // append a[j],x to a[j+1]
            vector<vector<int> > ss;
            for( int k = 0; k < a[j].size(); k++ ){
                if( a[j][k].first > x ) break;
                vector<vector<int> > &t=a[j][k].second;
                for (int l=0; l < t.size(); l++ ){
                    vector<int> tmp = t[l];
                    tmp.push_back( x );
                    ss.push_back( tmp );
                }
            }
            if( ss.empty() ) continue;
            vector<Pair>::iterator it = lower_bound( a[j+1].begin(), a[j+1].end(), Pair( x, vector<vector<int> >() ) );
            if( it != a[j+1].end() && it->first == x ){
                vector<vector<int> > &s = it->second;
                s.insert( s.end(), ss.begin(), ss.end() );
            }
            else a[j+1].insert( it, Pair( x, ss ) );
<span style="white-space:pre">	</span>    break; //@attention: @error: here  since a[j+1] already contains (x,ss), a[k<j+1] should not contains(x,ss) anymore.
        }
    }
    vector<vector<int> > tt;
    int i = n;
    while( i>=0 && a[i].empty() ) i--;
    if( i>=0 ){
        for( int j = 0 ; j < a[i].size(); j++ ){
            vector<vector<int> > &s=a[i][j].second;
            tt.insert( tt.end(), s.begin(), s.end() );
        }
		sort( tt.begin(), tt.end(), acmp );
        for( int k = 0; k < tt.size(); k++ ){
            // skip tt[k][0] = -9999
            for( int l = 1; l < tt[k].size(); l++ ){
                cout<< tt[k][l]<<" ";
            }
            cout<<endl;
        }
    }
}
int main(){
    int num[10] = { 1, 3, 2, 15, 10 };
    dp( num, 5 );
    return 0;
}


最短编辑距离:

len[0][i]=i; len[j][0]=j; //init

i=1 to n:   j=1 to m:

    len[i][j]= a[i-1]==b[j-1]? len[i-1][j-1] :min( len[i][j-1]+1, len[i-1][j]+1 );

最长公共连续子串:

len[0][i]=0,len[j][0]=0;

i=1 to n: j=1 to m:

    len[i][j]=a[i-1]==b[j-1]? len[i-1][j-1]+1: 0;


最长递增公共子序列:?  最长递增连续子序列(线性扫描即可)? 最长递增公共连续子串?


另外最长递增子序列有新的写法:

http://www.cnblogs.com/xwdreamer/archive/2011/06/21/2296995.html

O(n^2)的写法,好处是可以方便地用lst记录第一条最大长度递增序列:

len[i]=max (1, len[j]+1  if a[i]>=a[j])  j=0 to i-1.  len[i]记录以i为最后一个元素的最长递增序列长度, 也可以顺便记录i的前一个点j,用以打印第一条完整子序列。甚至是所有最长子序列。 即方便记录path。

#include <iostream>
#include <algorithm>
using namespace std;
#include <vector>
#include <map>
#include <stack>
// O(n^n) 方法获取
int dp(vector<int> &num){
	int n=num.size();
	vector<int> len(n, 1);
	vector<int> lst(n, -1);

	for(int i=1; i<n; i++){
		for(int j=0; j<i; j++){
			if(num[i]>=num[j]){
				// j may be the first pre-node of i in a max-length subseq
				if(len[j]+1>len[i])
				// record path
				len[i]=len[j]+1, lst[i]=j;	
			}
		}
	}
	stack<int> path;
	// get the first subseq that has the max length
	int mL=-1, mI=-1;
	for(int i=0; i<n; i++) if(len[i]>mL) mL=len[i], mI=i;
	// get the pre-node of i
	int i=mI;
	while(i>=0){
		path.push(i), i=lst[i];
	}
	// print path
	while(!path.empty()){
		cout<<num[path.top()]<<"->";path.pop();
	}cout<<endl;
	return mL;
}
int main(){
	vector<int>t={9,7,5,3,4,6,8,10,-1,2};
	cout<<dp(t)<<endl;
	return 0;
}


最短编辑距离: 增删改

#include <iostream>
#include <algorithm>
using namespace std;
#include <vector>
#include <map>
#include <stack>

int dp(vector<int> &a, vector<int> &b){
	int n=a.size(), m=b.size();
	vector<vector<int> > len(n+1, vector<int>(m+1, 0));
	// add
	for(int i=0; i<=m; i++) len[0][i]=i;
	// del
	for(int i=0; i<=n; i++) len[i][0]=i;
	for(int i=1; i<=n; i++){
		for(int j=1; j<=m; j++){
			// change
			if(a[i-1]==b[j-1]) len[i][j]=len[i-1][j-1];
			else{
				// add or del
				len[i][j]=min(len[i-1][j]+1, len[i][j-1]+1);
			}
		}
	}
	return len[n][m];
}
int main(){
	vector<int> a={1,2,3,4,5}, b={6,7,8,2,2};
	cout<<dp(a,b)<<endl;
	 a={1,2,3,4,5}, b={};
	cout<<dp(a,b)<<endl;
	 a={}, b={};
	cout<<dp(a,b)<<endl;
	return 0;
}


max递增序列枚举新的写法:

#include <iostream>
#include <algorithm>
#include <vector>
#include <list>
using namespace std;
typedef pair<int, vector<vector<int> > > Pair;
void dp(int num[], int n){
	vector< list < Pair > > a;
	vector< int > minN;
	for( int i=0; i<n; i++ ){
		int x = num[i];
		vector<int>::iterator it = upper_bound( minN.begin(), minN.end(), x );
		int idx = it == minN.end()? minN.size()-1 : it - minN.begin() - 1;
		if( it == minN.end() ){
			minN.push_back( x );
			a.push_back( list<Pair>() );
		}
		else 
			*it = x;

		if ( idx < 0 ){
			a[ 0 ].insert( a[0].begin(),
					Pair( x, vector<vector<int> >(1, vector<int>(1, x))));
		}
		else {
			Pair tmp( x, vector<vector<int> > () );
			list< Pair >::iterator it = a[idx].begin(), j = it;	
			while ( it!= a[idx].end() && it->first <= x ){
				tmp.second.insert( tmp.second.end(), it->second.begin(), it->second.end() );
				it ++;
			}
			for(int i=0; i<tmp.second.size(); i++) tmp.second[i].push_back(x);

			it = a[idx].begin();
			while ( it!= a[idx].end() && it->first < x ) it++;
			a[idx].erase( it, a[idx].end() );
			

			a[idx+1].insert( a[idx+1].begin(), tmp );
		}

	}

	int len=a.size();
	if( len == 0 ) return;
	for( list<Pair>::iterator it=a[len-1].begin(); it!=a[len-1].end(); it++ ){
		vector<vector<int> > &tmp=it->second;
		for(int j=0; j<tmp.size(); j++){
			for( int k=0; k<tmp[j].size(); k++ ){
				cout<<tmp[j][k]<<" ";
			}
			cout<<endl;
		}
	}
}

int main(){
	int num[15]={1,3,2,2,4,7,2,2,2,2,6};	
	dp(num, 11);
	return 0;
}



你可能感兴趣的:(【经典dp】n数填m空,括号序列取第k项,最长非降序列计数及枚举,最短编辑距离)