01背包(物品只取一次)
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m;
int dp[ 1000 ] [ 1000 ] ;
typedef struct
{
int w, v;
} S;
S s[ 1003 ] ;
int main ( )
{
ios:: sync_with_stdio ( false ) ;
cin >> n>> m;
for ( int i = 1 ; i <= n; i++ )
{
cin >> s[ i] . v >> s[ i] . w;
}
memset ( dp, 0 , sizeof ( dp) ) ;
for ( int j = m; j >= 0 ; j-- )
{
if ( j >= s[ 1 ] . w) dp[ j] = s[ 1 ] . v;
else dp[ j] = 0 ;
}
for ( int i = 2 ; i <= n; i++ )
{
for ( int j = m; j >= 0 ; j-- )
{
if ( j >= s[ i] . w) dp[ j] = max ( dp[ j] , dp[ j - s[ i] . w] + s[ i] . v) ;
}
} cout << dp[ m] << endl;
return 0 ;
}
完全背包(物品可取无限次)
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, v;
int dp[ 1003 ] ;
typedef struct
{
int v, w;
} S;
S s[ 1003 ] ;
int main ( )
{
ios:: sync_with_stdio ( false ) ;
cin >> n >> v;
for ( int i = 1 ; i <= n; i++ )
{
cin >> s[ i] . v >> s[ i] . w;
}
for ( int i = 1 ; i <= n; i++ )
{
for ( int j = s[ i] . w; j <= v; j++ )
{
dp[ j] = max ( dp[ j] , dp[ j - s[ i] . w] + s[ i] . v) ;
}
} cout << dp[ v] << endl;
return 0 ;
}
多重背包(每件物品可取p[i]次)
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m;
int c[ 1003 ] ;
int v[ 1003 ] ;
int a[ 1003 ] ;
int dp[ 100005 ] ;
# define M 100000000
void zeropack ( int cost, int value)
{
for ( int i = m; i >= cost; i-- )
{
dp[ i] = max ( dp[ i] , dp[ i - cost] + value) ;
}
}
void completepack ( int cost, int value)
{
for ( int i = cost; i <= m; i++ )
{
dp[ i] = max ( dp[ i] , dp[ i - cost] + value) ;
}
}
void mutiplepack ( int cost, int value, int amount)
{
int k;
if ( cost * amount >= m)
{
completepack ( cost, value) ;
return ;
}
k = 1 ;
while ( k < amount)
{
zeropack ( k * cost, k * value) ;
amount -= k;
k <<= 1 ;
}
zeropack ( amount * cost, amount * value) ;
}
int main ( )
{
ios:: sync_with_stdio ( false ) ;
while ( cin >> n >> m && ( n && m) )
{
for ( int i = 1 ; i <= n; i++ ) cin >> c[ i] ;
for ( int i = 1 ; i <= n; i++ ) cin >> v[ i] ;
for ( int i = 1 ; i <= n; i++ ) cin >> a[ i] ;
memset ( dp, 0 , sizeof ( dp) ) ;
for ( int i = 1 ; i <= n; i++ )
{
mutiplepack ( c[ i] , v[ i] , a[ i] ) ;
}
cout << dp[ m] << endl;
}
return 0 ;
}
二位费用背包
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
# define N 10000
int v[ N] ;
int w[ N] ;
int g[ N] ;
int dp[ N] ;
int n, ww, gg;
int main ( )
{
ios:: sync_with_stdio ( false ) ;
cin >> n >> m;
for ( int i = 1 ; i <= n; i++ ) cin >> v[ i] >> w[ i] >> g[ i] ;
for ( int i = 1 ; i <= n; i++ )
{
for ( int j = ww; j >= w[ i] ; j-- )
{
for ( int k = gg; k >= g[ i] ; k-- )
{
dp[ j] [ k] = max ( dp[ j] [ k] , dp[ j - w[ i] ] [ k - g[ i] ] + v[ i] ) ;
}
}
} cout << dp[ ww] [ gg] << endl;
return 0 ;
}
分组背包
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
# define N 10000 ;
int v[ N] ;
int w[ N] ;
int dp[ N] ;
int n, m;
int main ( )
{
ios:: sync_with_stdio ( false ) ;
cin >> n >> m;
for ( int i = 1 ; i <= n; i++ )
{
int s;
cin >> s;
for ( int k = 1 ; k <= s; k++ )
{
cin >> v[ k] >> w[ k] ;
}
for ( int j = m; j >= 0 ; i-- )
{
for ( int k = 1 ; k <= s; k++ )
{
if ( j >= w[ k] ) dp[ j] = max ( dp[ j] , dp[ j - w[ k] ] + v[ k] ) ;
}
}
} cout << dp[ m] << endl;
return 0 ;
}
dp悬线法
P1169 [ ZJOI2007] 棋盘制作
[ 参考代码]
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2005 ;
int lft[ N] [ N] , rgt[ N] [ N] , upp[ N] [ N] , res[ N] [ N] ;
int n, m, ans1 = - 100 , ans2 = - 100 ;
int main ( )
{
ios:: sync_with_stdio ( false ) ;
cin >> n >> m;
for ( int i = 1 ; i <= n; i++ ) {
for ( int j = 1 ; j <= m; j++ ) {
cin >> res[ i] [ j] ;
lft[ i] [ j] = rgt[ i] [ j] = j;
upp[ i] [ j] = 1 ;
}
}
for ( int i = 1 ; i <= n; i++ ) {
for ( int j = 2 ; j <= m; j++ ) {
if ( res[ i] [ j] != res[ i] [ j - 1 ] ) lft[ i] [ j] = lft[ i] [ j - 1 ] ;
}
}
for ( int i = 1 ; i <= n; i++ ) {
for ( int j = m - 1 ; j >= 1 ; j-- ) {
if ( res[ i] [ j] != res[ i] [ j + 1 ] ) rgt[ i] [ j] = rgt[ i] [ j + 1 ] ;
}
}
for ( int i = 1 ; i <= n; i++ ) {
for ( int j = 1 ; j <= m; j++ ) {
if ( i > 1 && res[ i] [ j] != res[ i - 1 ] [ j] ) {
upp[ i] [ j] = upp[ i - 1 ] [ j] + 1 ;
lft[ i] [ j] = max ( lft[ i] [ j] , lft[ i - 1 ] [ j] ) ;
rgt[ i] [ j] = min ( rgt[ i] [ j] , rgt[ i - 1 ] [ j] ) ;
}
int a = rgt[ i] [ j] - lft[ i] [ j] + 1 ;
int b = min ( a, upp[ i] [ j] ) ;
ans1 = max ( ans1, b * b) ;
ans2 = max ( ans2, a * upp[ i] [ j] ) ;
}
} cout << ans1 << endl;
cout << ans2 << endl;
return 0 ;
}
P4147 玉蟾宫
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m;
const int maxn = 1003 ;
int res[ maxn] [ maxn] , lft[ maxn] [ maxn] , rgt[ maxn] [ maxn] ;
int upp[ maxn] [ maxn] ;
int ans = - 1000 ;
int main ( )
{
ios:: sync_with_stdio ( false ) ;
cin >> n >> m;
char c;
for ( int i = 1 ; i <= n; i++ ) {
for ( int j = 1 ; j <= m; j++ ) {
cin >> c;
if ( c == 'F' ) res[ i] [ j] = 1 ;
else if ( c == 'R' ) res[ i] [ j] = 0 ;
if ( res[ i] [ j] )
{
lft[ i] [ j] = rgt[ i] [ j] = j;
upp[ i] [ j] = 1 ;
}
}
}
for ( int i = 1 ; i <= n; i++ ) {
for ( int j = 2 ; j <= m; j++ ) {
if ( res[ i] [ j] && res[ i] [ j] == res[ i] [ j - 1 ] ) lft[ i] [ j] = lft[ i] [ j - 1 ] ;
}
}
for ( int i = 1 ; i <= n; i++ ) {
for ( int j = m - 1 ; j >= 1 ; j-- ) {
if ( res[ i] [ j] && res[ i] [ j] == res[ i] [ j + 1 ] ) rgt[ i] [ j] = rgt[ i] [ j + 1 ] ;
}
}
for ( int i = 1 ; i <= n; i++ )
{
for ( int j = 1 ; j <= m; j++ )
{
if ( res[ i] [ j] )
{
if ( i > 1 && res[ i] [ j] == res[ i - 1 ] [ j] )
{
upp[ i] [ j] = upp[ i - 1 ] [ j] + 1 ;
lft[ i] [ j] = max ( lft[ i] [ j] , lft[ i - 1 ] [ j] ) ;
rgt[ i] [ j] = min ( rgt[ i] [ j] , rgt[ i - 1 ] [ j] ) ;
}
}
int a = rgt[ i] [ j] - lft[ i] [ j] + 1 ;
ans = max ( ans, a * upp[ i] [ j] ) ;
}
}
cout << 3 * ans << endl;
return 0 ;
}
多天完全背包
题目大意:小伟突然获得一种超能力,他知道未来 T 天 N 种纪念品每天的价格。
某个纪念品的价格是指购买一个该纪念品所需的金币数量,以及卖出一个该纪念品
换回的金币数量。每天,小伟可以进行以下两种交易无限次:
1 .任选一个纪念品,若手上有足够金币,以当日价格购买该纪念品;
2 .卖出持有的任意一个纪念品,以当日价格换回金币。每天卖出纪念品换回的金币
可以立即用于购买纪念品,当日购买的纪念品也可以当日卖出换回金币。当然,1 直
持有纪念品也是可以的。
T 天之后,小伟的超能力消失。因此他一定会在第 T 天卖出所有纪念品换回金币。
小伟现在有 M 枚金币,他想要在超能力消失后拥有尽可能多的金币。
参考代码:
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200 ;
int v[ maxn] [ maxn] ;
const int maxm = 10005 ;
int dp[ maxn] [ maxm] ;
int t, n, m;
int main ( )
{
ios:: sync_with_stdio ( false ) ;
cin >> t >> n >> m;
for ( int i = 1 ; i <= t; i++ )
{
for ( int j = 1 ; j <= n; j++ ) cin >> v[ i] [ j] ;
}
for ( int i = 2 ; i <= t; i++ )
{
memset ( dp, 0 , sizeof ( dp) ) ;
for ( int j = 1 ; j <= n; j++ )
{
for ( int k = 0 ; k <= m; k++ )
{
dp[ j] [ k] = dp[ j - 1 ] [ k] ;
if ( k >= v[ i - 1 ] [ j] )
dp[ j] [ k] = max ( dp[ j] [ k] , dp[ j] [ k - v[ i - 1 ] [ j] ] - v[ i - 1 ] [ j] + v[ i] [ j] ) ;
}
}
m += dp[ n] [ m] ;
}
cout << m << endl;
return 0 ;
}
到达型01背包
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, bl, ml;
int a[ 55 ] ;
int dp[ 55 ] [ 1003 ] ;
int main ( )
{
ios:: sync_with_stdio ( false ) ;
cin >> n >> bl >> ml;
for ( int i = 1 ; i <= n; i++ ) cin >> a[ i] ;
dp[ 0 ] [ bl] = 1 ;
for ( int i = 1 ; i <= n; i++ )
{
for ( int j = ml; j >= 0 ; j-- )
{
if ( j - a[ i] >= 0 ) dp[ i] [ j] = dp[ i] [ j] || dp[ i - 1 ] [ j - a[ i] ] ;
if ( j + a[ i] <= ml) dp[ i] [ j] = dp[ i] [ j] || dp[ i - 1 ] [ j + a[ i] ] ;
}
}
for ( int i = ml; i >= 1 ; i-- )
{
if ( dp[ n] [ i] )
{
cout << i << endl;
return 0 ;
}
}
cout << "-1" << endl;
return 0 ;
}
LIS(一个序列的最长上升子序列)
法一:n^ n做法:
for ( int i= 1 ; i<= n; i++ )
{
dp[ i] = 1 ;
for ( int j= 1 ; j< i; j++ )
{
if ( a[ j] < a[ i] ) dp[ i] = max ( dp[ i] , dp[ j] + 1 ) ;
}
}
法二:nlogn做法:
for ( int i= 1 ; i<= n; i++ )
{
cin>> a[ i] ;
dp[ i] = 0x3f3f3f3f ;
}
dp[ 1 ] = a[ 1 ] ;
int len= 1 ;
for ( int i= 2 ; i<= n; i++ )
{
int l= 0 , r= len, mid;
if ( a[ i] > dp[ len] ) dp[ ++ len] = a[ i] ;
else
{
while ( l< r)
{
mid= ( l+ r) / 2 ;
if ( dp[ mid] > a[ i] ) r= mid;
else l= mid+ 1 ;
}
dp[ l] = min ( a[ i] , dp[ l] ) ;
}
} cout<< len<< endl;
注意,事实上,nlogn做法偷了个懒,
并没有记录以每一个元素结尾的最长上升子序列的长度
LCS(两个序列的最长上升子序列)
用dp[ i] [ j] 数组来表示第一个串的前i位,第二个串的前j位的LCS的长度,
则状态转移方程:
若当前的A1[ i] == A2[ j] , dp[ i] [ j] = max ( dp[ i] [ j] , dp[ i- 1 ] [ j- 1 ] + 1 )
否则:dp[ i] [ j] = max ( dp[ i- 1 ] [ j] , dp[ i] [ j- 1 ] )
法一:n^ 2 做法:
int n, m;
int dp[ 1003 ] [ 1003 ] ;
int a1[ 1003 ] ;
int a2[ 1003 ] ;
cin>> n>> m;
for ( int i= 1 ; i<= n; i++ ) cin>> a1[ i] ;
for ( int i= 1 ; i<= m; i++ ) cin>> a2[ i] ;
for ( int i= 1 ; i<= n; i++ )
for ( int j= 1 ; j<= m; j++ )
{
dp[ i] [ j] = max ( dp[ i- 1 ] [ j] , dp[ i] [ j- 1 ] ) ;
if ( a1[ i] == a2[ j] )
dp[ i] [ j] = max ( dp[ i] [ j] , dp[ i- 1 ] [ j- 1 ] + 1 ) ;
}
cout<< dp[ n] [ m] << endl;
法二:(洛谷大佬nb!)
我们知道,最朴素的dp解LIS, LCS,复杂度都为O ( n^ 2 )
我们还知道,用另一种dp解LIS,复杂度O ( nlogn) ,
这种方法本质上是一种状态定义法,使其可以二分
LIS有O ( nlogn) 解法,LCS表示很嫉妒
于是,他一想:LIS求给定数列A的最长上升子序列,本质上是将
给定数列A. 与一个表示大小关系的数列(此时为1 到n递增排列)
的公共子序列,换言之LIS就是隐形的LCS, 那么LCS一定也可以
变为LIS,只要定义出表示大小关系的数列就可以了
,例如:求
2 5 4 3 1 和
1 2 4 5 3 的LCS
只需要在数列1 中重新定义大小关系,让2 < 5 < 4 < 3 < 1
代码如下:
int a[ 10003 ] ;
int b[ 10003 ] ;
int map[ 10004 ] ;
int dp[ 10004 ] ;
int n;
cin>> n;
for ( int i= 1 ; i<= n; i++ )
{
cin>> a[ i] ;
map[ a[ i] ] = i; 令2 < 5 < 4 < 3 < 2
}
for ( int i= 1 ; i<= n; i++ )
{
cin>> b[ i] ;
dp[ i] = 0x3f3f3f3f ;
}
int len= 0 ;
dp[ 0 ] = 0 ;
for ( int i= 1 ; i<= n; i++ )
{
int l= 0 , r= len, mid;
if ( map[ b[ i] ] > dp[ len] ) dp[ ++ len] = map[ b[ i] ] ;
else
{
while ( l< r)
{
mid= ( l+ r) / 2 ;
if ( dp[ mid] > map[ b[ i] ] ) r= mid;
else l= mid+ 1 ;
} dp[ l] = min ( dp[ l] , map[ b[ i] ] ) ;
}
}
cout<< len<< endl;
LICS
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int dp[ 3010 ] [ 3010 ] ;
int a[ 3010 ] ;
int b[ 3010 ] ;
int n;
int main ( )
{
ios:: sync_with_stdio ( false ) ;
cin >> n;
for ( int i = 1 ; i <= n; i++ ) cin >> a[ i] ;
for ( int i = 1 ; i <= n; i++ ) cin >> b[ i] ;
for ( int i = 1 ; i <= n; i++ )
{
int maxx = 1 ;
for ( int j = 1 ; j <= n; j++ )
{
dp[ i] [ j] = dp[ i - 1 ] [ j] ;
if ( b[ j] < a[ i] ) maxx = max ( maxx, dp[ i - 1 ] [ j] + 1 ) ;
if ( a[ i] == b[ j] ) dp[ i] [ j] = max ( dp[ i] [ j] , maxx) ;
}
}
int ans = 0 ;
for ( int i = 1 ; i <= n; i++ )
{
ans = max ( ans, dp[ n] [ i] ) ;
}
cout << ans << endl;
return 0 ;
}