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;
}