SMU 2021 新生培训 DP选讲

1216

一道比较简单的递推题目

f[i][j] += max( f[i-1][j] , f[i-1][j-1])

#include<bits/stdc++.h>
using namespace std;

const int N = 1010;
int r,a[N][N] = {},maxx = 0;

int main()
{
	cin >> r;
	for(int i = 1 ;i <= r;i++)
	{
		for(int j = 1;j <= i;j++) cin >> a[i][j];
	}
	for(int i = 2;i <= r;i++)
	{
		for(int j = 1;j <= i;j++)	
          		a[i][j] += max(a[i-1][j],a[i-1][j-1]);		
	}
	for(int i = 1;i <= r;i++) maxx = max(maxx,a[r][i]);
	cout << maxx <<endl;
	return 0;
}   

1048

模板题

#include<bits/stdc++.h>
using namespace std;


int n,m;
int v[105],w[105];
int f[1005] = {};

int main()
{
	cin >> m >> n;
	for(int i = 1;i <= n;i++) cin >> v[i] >> w[i];
	for(int i = 1;i <= n;i++)
	{
		for(int j = m;j >= 0;j--)
		{
			if(j >= v[i]) f[j] = max(f[j],f[j-v[i]]+w[i]);
		}
	}
	cout << f[m] << endl;
	
	return 0;
}

补充一下朴素做法

#include<bits/stdc++.h>
using namespace std;

int n , m , v[105] , w[105] , f[105][1005];

int main()
{
    cin >> m >> n ;
    for( int i = 1 ; i <= n ; i ++) cin>> v[i] >> w[i];
    for( int i = 1 ; i <= n ; i ++ )
    {
        for( int j = 1 ; j < v[i] ; j ++ ) f[i][j] = f[i-1][j];
        for( int j = v[i] ; j <= m ; j ++ )
            f[i][j] = max( f[ i - 1 ][ j - v[i] ] + w[i] , f[i-1][j] );
    }
    cout << f[n][m] << endl;
    return 0;
}

1616

模板题目

#include<bits/stdc++.h>
using namespace std;

const int T = 100005,N = 10005;
int n,t;
int w[N] = {},v[N] = {},f[T] = {};

int main()
{
	cin >> t >> n;
	for(register int i = 1;i <= n;i++) cin >> v[i] >> w[i];	
	for(register int i = 1;i <= t;i++)
	{
		for(register int j = 0;j <= n;j++)
		{
			if(i-v[j] < 0) continue;
			f[i] = max(f[i],f[i-v[j]]+w[j]);
		}
	}
	cout << f[t] << endl;
	return 0;
}

1802

01背包题目

f[i][j]表示第i个人当我的用药量为j时候的所获得的最大经验

j>=use[i]时,可赢也可不赢

f[i][j] = max(f[i-1][j]+lose[i] , f[i-1][j-use[i]] + win[i])

j<use[i]时,只能输这时选择用药量为0是最优解

f[i][j] = f[i-1][j] + lose[i]

可以滚动数组压缩一维空间,压缩空间必须倒序枚举

#include<iostream>
#define LL long long
using namespace std;

const int N = 1e3 + 5;
int n , m , lose[N] , win[N] , use[N];
LL f[N];


int main()
{
    cin >> n >> m;
    for( int i = 1 ; i <= n ; i ++ ) 
        cin >> lose[i] >> win[i] >> use[i];
        
    for(int i = 1 ; i <= n ; i ++ )
    {
    	for( int j = m ; j >= use[i] ; j -- ) f[j] = max( f[j] + lose[i] , f[ j - use[i] ] + win[i] );
    	for( int j = use[i] - 1 ; j >= 0 ; j -- ) f[j] += lose[i];
	}
    cout << f[m] * 5 << endl;
    return 0;
}

1434

记忆化搜索

v[i][j]表示从(i,j)出发的最大长度

从每个点出发用dfs跑一遍最长路即可

#include<bits/stdc++.h>
#define fx ( x + dx[i] )
#define fy ( y + dy[i] )
using namespace std;

const int N = 105;
const int dx[] = { 0 , 0 , 1 , -1 } , dy[] = { 1 , -1 , 0 , 0 };
int n , m , a[N][N] , v[N][N] , ans;

int dfs( int x , int y )
{
	if( v[x][y] != 1 ) return v[x][y];
	for( int i = 0 ; i < 4 ; i ++ ) 
	{
		if( fx < 1 || fy < 1 || fx > n || fy > m || a[fx][fy] >= a[x][y] ) continue;
		v[x][y] = max( v[x][y] , dfs( fx , fy ) + 1 );
	} 
	return v[x][y];
}
int main()
{
	cin >> n >> m;
	for( int i = 1 ; i <= n ; i ++ )
	{
		for( int j = 1 ; j <= m ; j ++ ) cin >> a[i][j] , v[i][j] = 1;
	}
	for( int i = 1 ; i <= n ; i ++ )
	{
		for( int j = 1 ; j <= m ; j ++ ) ans = max( ans , dfs( i , j ) ); 
	}
	cout << ans << endl;
	return 0;
}

2196

从入度为零的点走,走到出度为零的点即可

#include<bits/stdc++.h>
using namespace std;

const int N = 25;
int n , val[N] , ans , ans_len , ans_str[N] , path[N] , v[N] , in[N] , out[N] ;
vector< int > e[N];

void dfs( int x , int step , int sum)
{
    path[step] = x;
    if( out[x] == 0 )
    {
        if( ans >= sum ) return ;
        ans = sum , ans_len = step;
        for( int i = 1 ; i <= step ; i ++ ) ans_str[i] = path[i];
        return ;
    }
    for( auto it : e[x] ) dfs( it , step + 1 , sum + val[it] );
}

int main()
{
    cin >> n ;
    for( int i = 1 ; i <= n ; i ++ ) cin >> val[i];
    for( int i = 1 ; i <= n ; i ++ )
    {
        for( int j = i + 1 , x ; j <= n ; j ++ )
        {
            cin >> x;
            if( x == 1 ) e[i].push_back( j ) , out[i] ++ , in[j] ++ ;
        }
    }
    for( int i = 1 ; i <= n ; i ++ )
    {
        if( in[i] > 0 ) continue;
        dfs( i ,  1 , val[i] );
    }
    for( int i = 1 ; i <= ans_len ; i ++ ) cout << ans_str[i] << " ";
    cout << endl << ans << endl;
    return 0;
}

1077

f[i][j]表示前i种花,一共放[j]种的所有情况

\(f[i][j]=\sum_{k=0}^{min(j,a[i])}f[i-1][j-k]\)

#include <bits/stdc++.h>
using namespace std;

const int N = 105 , mod = 1e6+7;
int n , m ;
int f[N][N] , a[N];


int main()
{
    cin >> n >> m;
    for( int i = 1 ; i <= n ; i ++ ) cin >> a[i];
    f[0][0] = 1;
    for( int i = 1 ; i <= n ; i ++ )
    {
        for( int j = 0 ; j <= m ; j ++ )
        {
            for( int k = 0 ; k <= min( j , a[i] ) ; k ++ )
            {
                f[i][j] = ( f[i][j] + f[ i - 1 ][ j - k ] ) % mod;
            }
        }
    }
    cout << f[n][m] << endl;
    return 0;
}

本题还能继续优化,因为f[i]只和f[i-1]有关,所以可以压缩以为空间

#include <bits/stdc++.h>
using namespace std;

const int N = 105 , mod = 1e6+7;
int n , m ;
int f[N] , a[N];


int main()
{
    cin >> n >> m;
    for( int i = 1 ; i <= n ; i ++ ) cin >> a[i];
    f[0] = 1;
    for( int i = 1 ; i <= n ; i ++ )
    {
        for( int j = m ; j >= 0 ; j -- )
        {
            for( int k = 1 ; k <= min( j , a[i] ) ; k ++ )
                f[j] = ( f[j] + f[j-k] ) % mod;
        }
    }
    cout << f[m] << endl;
    return 0;
}
posted @ 2021-11-21 21:12  PHarr  阅读(50)  评论(0编辑  收藏  举报