第一次写状态压缩DP。。参考了 kuangbin 大神的代码。。学习了
感觉状压用位运算的技巧比较多,而且因为数据一般都是 n <= 16 级别的,好多操作都可以做的比较暴力。。
dp[i] = min(dp[j] + penalty), j 是 i 只差一个元素的子状态
初始化 dp[0] = 0, dp[i] = inf (i ≠ 0) 可以对所有状态进行一次循环,并更新由该状态加一个 “1” 得到的新状态。。
由于要输出答案顺序,所以要有一个 pre 数组
另外输出答案的函数用到了递归的技巧,学习了。。
具体在代码中。。
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
int n;
typedef struct CRS {
string name;
int dd, ex;
}CRS;
int dp[1<<15], pre[1<<15];
CRS crs[15];
void printAns(int s)
{
if(s == 0) return;
int a = 0;
for(int i = 0; i < n; i++) {
if((s & (1<<i)) && !(pre[s] & (1<<i))) {
a = i;
break;
}
}
printAns(pre[s]);
cout << crs[a].name << endl;
}
int main()
{
int t;
cin >> t;
while(t--) {
cin >> n;
for(int i = 0; i < n; i++)
cin >> crs[i].name >> crs[i].dd >> crs[i].ex;
for(int i = 1; i < (1<<n); i++)
dp[i] = inf; //因为要求的是扣分的最小值,所以没用过的 dp 值应该是 inf
dp[0] = 0;
for(int s = 0; s < (1<<n); s++) {
for(int i = 0; i < n; i++) {
if(s & (1<<i)) continue;
int pen = 0;
for(int j = 0; j < n; j++) {
if(s & (1<<j))
pen += crs[j].ex;
}
pen += crs[i].ex;
if(pen > crs[i].dd) pen = pen - crs[i].dd;
else pen = 0;
if(dp[s|(1<<i)] > dp[s] + pen) {
dp[s|(1<<i)] = dp[s] + pen;
pre[s|(1<<i)] = s;
}
}
}
cout << dp[(1<<n) - 1] << endl;
printAns((1<<n) - 1);
}
return 0;
}