区间dp
区间的dp大多都是由区间构成的,而我们解题的方法就是把大区间化成小区间,然后对小区间进行处理最后得到我们想要的答案。大部分的区间dp都有明显的特点,我们需要考虑在什么条件下大区间可以转化成小区间然后再找到边界条件,这样就可以列出状态转移方程了。
区间dp经典代码模板
for ( int len = 2 ; len <= n; len++ )
{
for ( int i = 1 ; i < n; i++ )
{
int j = i+ len- 1 ;
if ( j> n)
break ;
for ( int k = i; k <= j; k++ )
f[ i] [ j] = min ( f[ i] [ j] , f[ i] [ k] + f[ k+ 1 ] [ j] + w[ i] [ j] ) ;
}
}
通过做的这些题发现,简单题目和难题的区别在于难题相对于简单题更加的复杂,大体的框架和简单题是一样的,但是难题需要在里面添加更多的细节。一不小心就会出错,而且有的细节想不到,想不到要加上这个。
这个经典的代码还可以进行优化,使他从O(n^3)优化到 O(n^2),就是在原来的基础上再多开一个数组,在枚举分割点的时候缩小查询范围,用空间换取时间。
优化的代码
for ( int len = 2 ; len <= n; len++ )
{
for ( int i = 1 ; i<= n; i++ )
{
int j = i+ len- 1 ;
if ( j> n)
break ;
for ( int k = s[ i] [ j- 1 ] ; k<= s[ i+ 1 ] [ j] ; k++ )
{
if ( f[ i] [ j] > f[ i] [ k] + f[ k+ 1 ] [ j] + w[ i] [ j] )
{
f[ i] [ j] = f[ i] [ k] + f[ k+ 1 ] [ j] + w[ i] [ j] ;
s[ i] [ j] = k;
}
}
}
}
多重背包二进制优化模板(这是主体框架,根据需要自行修该)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#define inf 0x3f3f3f3f
#define N 1000
using namespace std;
int V;
int m[ N] , c[ N] , w[ N] , f[ N] ;
int max ( int a, int b) { return a> b? a: b; }
void ZeroOnePack ( int cost, int weight)
{
for ( int v = V; v >= cost; v-- )
f[ v] = max ( f[ v] , f[ v- cost] + weight) ;
}
void CompletePack ( int cost, int weight)
{
for ( int v = cost; v <= V; v++ )
f[ v] = max ( f[ v] , f[ v- cost] + weight) ;
}
void MultiplePack ( int cost, int weight, int amount)
{
int k;
if ( cost* amount>= V)
{
CompletePack ( cost, weight) ;
return ;
}
k = 1 ;
while ( k< amount)
{
ZeroOnePack ( k* cost, k* weight) ;
amount = amount- k;
k = k * 2 ;
}
ZeroOnePack ( amount* cost, amount* weight) ;
}
int main ( )
{
int n;
scanf ( "%d %d" , & n, & V) ;
for ( int i = 1 ; i <= n; i++ )
scanf ( "%d %d %d" , & m[ i] , & c[ i] , & w[ i] ) ;
for ( int i = 1 ; i <= n; i++ )
MultiplePack ( c[ i] , w[ i] , m[ i] ) ;
printf ( "%d\n" , f[ V] ) ;
return 0 ;
}