思路:
状态压缩 dp 。一共十五种课程,我们使用一个整型数的二进制表示已经上过的课程。如000 000 000 000 101 (数5 )代表当前状态已经上完了第一、三 种课程。 其实严格来说,我认为它更多的是一种暴力贪心 的思想。因为:我们需要依次从小到大 枚举所有的状态,每一种状态都只需要使用已经推过 的子状态,并且在其中选择当前最优 状态更新自身。 注意:状压 dp 都会用到位运算 ,位运算必须要一步一括号
代码:
struct SUB{
string name;
int d;
int w;
} sub[ 20 ] ;
struct STATE{
int reduce;
int days;
int prestate;
int presub;
} state[ maxn] ;
#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#include <stack>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = ( 1 << 15 ) + 5 ;
int N;
struct SUB{
string name;
int d;
int w;
} sub[ 20 ] ;
struct STATE{
int reduce;
int days;
int prestate;
int presub;
} state[ maxn] ;
stack< string> S;
void INIT ( ) {
memset ( state , 0 , sizeof ( state) ) ;
for ( int i= 1 ; i< maxn; i++ ) {
state[ i] . reduce = INF;
}
return ;
}
void SOLVE ( ) {
for ( int i= 1 ; i< ( 1 << N) ; i++ ) {
for ( int j= N; j; j-- ) {
int pre = 1 << ( j- 1 ) ;
if ( i & pre) {
int prei = i - pre;
int reduce = max ( 0 , state[ prei] . days + sub[ j] . w - sub[ j] . d) ;
if ( state[ prei] . reduce + reduce < state[ i] . reduce) {
state[ i] . reduce = state[ prei] . reduce + reduce;
state[ i] . days = state[ prei] . days + sub[ j] . w;
state[ i] . prestate = prei;
state[ i] . presub = j;
}
}
}
}
return ;
}
void OUTPUT ( ) {
int cur = ( 1 << N) - 1 ;
cout<< state[ cur] . reduce<< endl;
while ( cur) {
int ser = state[ cur] . presub;
cur = state[ cur] . prestate ;
S. push ( sub[ ser] . name) ;
}
while ( S. size ( ) ) {
cout<< S. top ( ) << endl;
S. pop ( ) ;
}
return ;
}
int main ( ) {
int T; cin>> T;
while ( T-- ) {
INIT ( ) ;
cin>> N;
for ( int i= 1 ; i<= N; i++ )
cin>> sub[ i] . name>> sub[ i] . d>> sub[ i] . w;
SOLVE ( ) ;
OUTPUT ( ) ;
}
return 0 ;
}
二刷:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stack>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = ( 1 << 15 ) + 5 ;
int T , N;
struct COURSE{
string name;
int D;
int W;
} co[ maxn] ;
struct DP{
int pre;
int day;
int penalty;
int preid;
} dp[ maxn] ;
int main ( ) {
cin>> T;
while ( T-- ) {
memset ( dp , INF , sizeof ( dp) ) ;
dp[ 0 ] . penalty = dp[ 0 ] . day = dp[ 0 ] . pre = dp[ 0 ] . preid = 0 ;
cin>> N;
for ( int i= 1 ; i<= N; i++ )
cin>> co[ i] . name>> co[ i] . D>> co[ i] . W;
for ( int i= 1 ; i< ( 1 << N) ; i++ ) {
int k= 1 ;
for ( int j= 1 ; k<= i; j++ , k<<= 1 ) {
if ( k & i) {
int pre = i- k;
if ( dp[ pre] . penalty + dp[ pre] . day + co[ j] . W - co[ j] . D <= dp[ i] . penalty) {
dp[ i] . penalty = dp[ pre] . penalty + max ( 0 , dp[ pre] . day + co[ j] . W - co[ j] . D) ;
dp[ i] . day = dp[ pre] . day + co[ j] . W;
dp[ i] . pre = pre;
dp[ i] . preid = j;
}
}
}
}
int cur = ( 1 << N) - 1 ;
cout<< dp[ cur] . penalty<< endl;
stack< string> S;
while ( cur) {
int id = dp[ cur] . preid;
S. push ( co[ id] . name) ;
cur = dp[ cur] . pre;
}
while ( S. size ( ) ) {
cout<< S. top ( ) << endl;
S. pop ( ) ;
}
}
return 0 ;
}