[DP复习]

一天8题,6道水题

做法一:考虑在完全背包上加点东西
做法二:在完全背包第一次转移时加值

就是个状压DP模板

#include<bits/stdc++.h>
using namespace std ;
inline int read(){
    int s=0 ; char g=getchar() ; while(g>'9'||g<'0')g=getchar() ;
    while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;
}
int N , M , state[ 4096 ] , field[ 13 ] , mod = 100000000 , dp[ 13 ][ 4096 ] ;
int main(){
    freopen("chess.in","r",stdin);
    freopen("chess.out","w",stdout);
    N = read() , M = read() ; int m1 ;
    for( int i = 1 ; i <= N ; ++i )
        for( int j = 1 ; j <= M ; ++j )
            m1 = read() , field[ i ] = ( field[ i ]<<1 ) + (m1^1) ;
    for( int i = 0 ; i < ( 1<<M ) ; ++i )state[ i ] = ( ( !( (i<<1)&i) )&&( !(i&(i>>1) )) ) ;
    dp[ 0 ][ 0 ] = 1 ;
    for( int i = 1 ; i <= N ; ++i )
        for( int j = 0 ; j < (1<<M) ; ++j ){//枚举状态
            if( !state[ j ] )continue ;
            if( j&field[ i ] )continue ;
            for( int k = 0 ; k < (1<<M) ; ++k ){
                if( k&j )continue ;
                dp[ i ][ j ] = ( dp[ i-1 ][ k ] + dp[ i ][ j ] )%mod;
            }
        }
    int ans = 0 ;
    for( int i = 0 ; i < ( 1<<M )  ; ++i )ans = (dp[ N ][ i ] + ans)%mod;
    cout<<ans ;
}

悬线法入门题,不懂得请看ssw02的 悬线法总结

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1005 ;
inline int read(){
    int s=0 ; char  g=getchar() ; while( g>'9'||g<'0' )g=getchar() ;
    while( g>='0'&&g<='9')s=s*10+g-'0',g=getchar(); return s ;
}
int N , M , a[ MAXN ][ MAXN ] , flag = 0 ;
int l[ MAXN ][ MAXN ] , r[ MAXN ][ MAXN ] , up[ MAXN ][ MAXN ] , ans = 0 ;
int main(){
    freopen("question.in","r",stdin);
    freopen("question.out","w",stdout);
    N = read() , M = read() ;
    for( int i = 1 ; i <= N ; ++i )
        for( int j = 1 ; j <= M ; ++j )
            a[ i ][ j ] = read() , l[ i ][ j ] = r[ i ][ j ] = j , up[ i ][ j ] = 1 , flag = max(flag,a[i][j]) ;
    for( int i = 1 ; i <= N ; ++i )
        for( int j = 2 ; j <= M ; ++j )
            if( a[i][j] && a[i][j-1] )l[i][j] = l[i][j-1] ;
    for( int i = 1 ; i <= N ; ++i )
        for( int j = M-1 ; j >= 1 ; --j )
            if( a[i][j] && a[i][j+1] )r[i][j] = r[i][j+1] ;
    for( int i = 1 ; i <= N ; ++i )
        for( int j = 1 ; j <= M ; ++j ){
            if( i > 1 )
                if( a[i][j] && a[i-1][j] ){
                    l[i][j] = max( l[i][j] , l[i-1][j] ) ;
                    r[i][j] = min( r[i][j] , r[i-1][j] ) ;
                    up[i][j] = up[i-1][j] + 1 ;
                }
            int lon = r[i][j] - l[i][j] + 1 ;
            ans = max( ans , lon*up[i][j] ) ;
        }
    if( !flag )cout<<0;
    else cout<<ans;
}

DAG上DP , 按拓扑排序保证无后效性即可

#include<bits/stdc++.h>
using namespace std ;
const int MAXN = 100005 ;
#define ll long long
inline int read(){
    int s=0,w=1 ; char g=getchar() ; while(g>'9'||g<'0'){if(g=='-')w=-1;g=getchar();} ;
    while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s*w ;
}
int N , M , tot = 1 , head[ MAXN ] , to[ MAXN*20 ] , nex[ MAXN*20 ] , a[ MAXN ] , du[ MAXN ] , ru[ MAXN ] ;
ll dis[ MAXN ] ;
bool vis[ MAXN ] ;
queue<int>q ;
void add( int x , int y ){
    to[ ++tot ] = y , nex[ tot ] = head[ x ] , head[ x ] = tot ;
}
void  toposort(){
    while( !q.empty() ){
        int u = q.front() ; q.pop() ; vis[ u ] = false ;
        for( int i = head[ u ] ; i ; i = nex[ i ] ){
            dis[ to[i] ] = max( dis[ u ]+(ll)a[ to[i] ] , dis[ to[i] ] ) ;
            du[ to[i] ]-- ;
            if( du[ to[i] ]==0 && vis[ to[i] ]==0 ){
                q.push( to[i] ) ; vis[ to[i] ] = true ;
            }
        }
    }
}
int main(){
    freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);
    N = read() , M = read() ; int m1 , m2 ;
    for( int i = 1 ; i <= N ; ++i )a[ i ] = read() , dis[ i ] = -2000000005  ;
    for( int i = 1 ; i <= M ; ++i ){
        m1 = read() , m2 = read() , du[ m1 ]++ , ru[ m2 ]++ ;
        add( m2 , m1 ) ;
    }
    for( int i = 1 ; i <= N ; ++i )
       if( !du[ i ] ){
          vis[ i ] = true , dis[ i ] = (ll)a[ i ] ; q.push( i ) ;
       }
    toposort() ;
    ll ans = -2000000005 ;
    for( int i = 1 ; i <= N ; ++i )
       if( !ru[ i ] )
           ans = max( ans , dis[ i ] ) ;
    cout<<ans ;
}

我们考虑到,每一位的影响只和当前位置的字符和先前一位的字符有关,然后就是分情况的线性DP

#include<bits/stdc++.h>
using namespace std ;
#define ll long long
inline ll read(){
    ll s=0,w=1 ; char g=getchar() ; while(g>'9'||g<'0'){if(g=='-')w=-1;g=getchar();}
    while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s*w ;
}
char s[ 200005 ] ;
ll  N ,  val[ 200005 ] , dp[ 200005 ][ 2 ] ;
inline ll max( ll x , ll y ){
    if( x > y )return x ;
    else return y ;
}
void  updata( int i , int x ){
    if( x ){
        if( dp[ i-1 ][ 0 ] != -1 )dp[ i ][ 1 ] = dp[ i-1 ][ 0 ] + val[ i ] ;
        if( dp[ i-1 ][ 1 ] != -1 )dp[ i ][ 1 ] = max( dp[ i ][ 1 ] , dp[ i-1 ][ 1 ] ) ;
        return ;
    }
    if( dp[ i-1 ][ 1 ] != -1 )dp[ i ][ 0 ] = dp[ i-1 ][ 1 ] + val[ i ] ;
    if( dp[ i-1 ][ 0 ] != -1 )dp[ i ][ 0 ] = max( dp[ i ][ 0 ] , dp[ i-1 ][ 0 ] ) ;
}
int main(){
    freopen("gray.in","r",stdin);
    freopen("gray.out","w",stdout);
    scanf("%s",s) ;
    N = strlen(s) ; dp[ 0 ][ 1 ] = -1 ;
    for( int i = 1 ; i <= N ; ++i )val[ i ] = read() , dp[ i ][ 1 ] = dp[ i ][ 0 ] = -1e10 ;//坑人
    for( int i = 1 ; i <= N ; ++i ){
        if( s[i-1] == '0' ){
            dp[ i ][ 1 ] = -1 ;
            updata( i , 0 ) ;
        }
        else if( s[i-1] == '1' ){
            dp[ i ][ 0 ] = -1 ;
            updata( i , 1 ) ;
        }
        else{
            updata( i , 0 ) ;
            updata( i , 1 ) ;
        }
    }
    cout<<max( dp[ N ][ 1 ] , dp[ N ][ 0 ] ) ;
    /*for( int i = 1 ; i <= N ; ++i )cout<<val[ i ]<<" ";cout<<endl ;
    for( int i = 1 ; i <= N ; ++i )cout<<dp[ i ][ 0 ]<<" " ;cout<<endl ;
    for( int i = 1 ; i <= N ; ++i )cout<<dp[ i ][ 1 ]<<' ' ;*/
    return 0 ;
}

RMQ问题,线段树或者树状数组或者ST表维护(留个ST表板子)

#include<bits/stdc++.h>
using namespace std ;
const int MAXN = 100005 ;
int N , M , a[ MAXN ] , st[ MAXN ][ 20 ] , log_a[ MAXN ] ;
inline int read(){
    int s=0 ,w=1; char g=getchar() ; while(g>'9'||g<'0'){if(g=='-')w=-1;g=getchar();}
    while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s*w ;
}
void  prepare(){
    for( int i = 1 ; i <= 17 ; ++i )
        for( int u = 1 ; u + (1<<i) - 1 <= N ; ++u )
            st[ u ][ i ] = min( st[ u ][ i-1 ] , st[ u + ( 1<<i-1 ) ][ i-1 ]  ) ;
}
int  ask( int l , int r ){
    int k = log_a[ r-l+1 ] ;
    return min( st[ l ][ k ] , st[ r-(1<<k)+1 ][ k ] ) ;
}
int main(){
    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);
    N = read() ;  int m1 , m2 ;  log_a[0]=-1;
    for( int i = 1 ; i <= N ; ++i )st[ i ][ 0 ] = read() , log_a[i]=log_a[ i>>1 ] +1 ;
    M = read() ;prepare() ;
    for( int i = 1 ; i <= M ; ++i ){
        m1 = read() , m2 = read() ;
        printf("%d\n",ask(m1,m2) ) ;
    }
    return 0 ;
}

好题,我们考虑到从每个房间数的人并不好维护,然后考虑到经过的人只能是从左右两个位置进入和出去

具体写代码里了

#include<bits/stdc++.h>
using namespace std ;
#define ll long long
const int MAXN = 1e5+5 ;
inline ll read(){
    ll s=0 ; char g=getchar() ; while(g>'9'||g<'0')g=getchar() ;
    while( g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;
}
ll N , dp[ MAXN ][ 15 ] , a[ MAXN ] ;
inline ll min( ll x , ll y ){
    return ( x<y )?x:y ;
}
inline bool check( int x ){
    if( x==0||x==4||x==7 )return true ;
    else return false ;
}
int main(){// dp[i][0 ~ 14]表示从1~i再向外走出去-7(负数就是进来)~7个人就能使1~i房间合法的最小费用.
    freopen("hotel.in","r",stdin);
    freopen("hotel.out","w",stdout);
    N = read() ;
    for( int i = 1 ; i <= N ; ++i )a[ i ] = read() ;
    for( int i = 0 ; i <= N ; ++i )
        for( int j = 0 ; j <= 14 ; ++j )dp[ i ][ j ] = 1e10 ;
    dp[ 0 ][ 7 ] = 0 ;
    for( int i = 1 ; i <= N ; ++i )
        for( int j = 0 ; j <= 14 ; ++j )
            for( int k = 0 ; k <= 14 ; ++k ){
                int in = j - 7 , out = k - 7 ;//存在负值 7为基数位
                if( check( a[ i ] + in - out ) ){
                    dp[ i ][ k ] = min( dp[ i ][ k ] , dp[ i-1 ][ j ] + abs(in) ) ;
                }
            }
    cout<<( (dp[ N ][ 7 ]==1e10 )?-1:dp[ N ][ 7 ] ) ;
}

区间DP , 移项后将 dp[ i ][ k ] - k * val[ i ]丢到单调队列中维护

#include<bits/stdc++.h>
using namespace std ;
const int MAXN = 16005 ;
#define ll long long
inline ll read(){
    ll s=0,w=1 ; char g=getchar() ; while(g>'9'||g<'0'){if(g=='-')w=-1;g=getchar();}
    while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s*w ;
}
ll N , M , ans , dp[ 105 ][ MAXN ] , q[ MAXN ] ;
struct ap{
    ll l , val , pre ;
}t[ 105 ] ;
inline bool cmp( ap x , ap y ){
    return x.pre < y.pre ;
}
inline ll max( ll x , ll y ){
    return ( x > y )?x:y ;
}
int main(){
    freopen("gift.in","r",stdin) ;
    freopen("gift.out","w",stdout);
    N = read() , M = read() ;
    for( int i = 1 ; i <= M ; ++i )
        t[ i ].l = read() , t[ i ].val = read() , t[ i ].pre = read() ;
    sort( t+1 , t+M+1 , cmp ) ;
    for( int i = 1 ; i <= M ; ++i ){
        int head = 1 , tail = 0 ;
        q[ ++tail ] = max( t[ i ].pre - t[ i ].l , 0 ) ;
        for(int j = 1; j <= N; ++j ){
            dp[i][j] = max( dp[i - 1][j], dp[i][j - 1] ) ;//不放
            if( j >= t[ i ].pre + t[ i ].l )continue ;//放
            while( head <= tail && q[ head ] + t[ i ].l < j )++head ;
            if( j < t[ i ].pre ){
                int tmp = dp[ i-1 ][ j ] - j*t[ i ].val ;//移项后  dp[ i-1 ][ k ] - k*val[ i ]
                while( head <= tail && tmp > dp[ i-1 ][ q[tail] ] - t[i].val*q[tail] )--tail ;//cz
                q[ ++tail ] = j ; continue ;
            }
            else dp[i][j] = max( dp[i][j] , dp[ i-1 ][ q[head] ] + (j-q[head])*t[i].val ) ;
        }
    }
    cout<<dp[M][N] ;
}
01-22 17:24