dp~状态压缩
dp+状态压缩: 一个数组即可保存状态。但是有这样的一些题目,它们具有DP问题的特性,但是状态中所包含的信息过多,如果要用数组来保存状态的话需要四维以上的数组。于是,我们就需要通过状态压缩来保存状态,而使用状态压缩来保存状态的DP就叫做状态压缩DP。 (感觉就是状态过多了,就要简化状态的表达,就是用二进制的操作的简化。。)
题目大意: 有n(n<=15)科作业,每科作业都有一个最迟上交时间di和完成的花费时间ci。每科作业迟几天上交就会有(损失1天1单位)。
求:完成所有作业的最少损失(要输出作业完成顺序)。
思路:dp就要有状态的存储。15科作业的完成情况全排那么状态就有15!种,数组开不起~
n最大为15。可以用15位的二进制数来表示每种作业的完成情况(1完成0未完成)
这样状态就只有 2^15 种,还可以接受。。。。。
#include <iostream> #include <cstdio> #include <cstring> using namespace std;
const int maxn=(1<<15)+5; struct node{ int c, d; //c代表花费时间, d代表最后期限 char na[205]; } work[16]; struct node1{ int cost, reduce, pre; //cost代表花费时间 reduce代表损失,pre记录前一个状态 } dp[maxn]; bool v[maxn]; void print(int a){ //递归输出顺序 int next = dp[a].pre^a; if(dp[a].pre==-1) return; print(dp[a].pre); int id=0; //找a的二进制数最右边的1的位置 next >>= 1; while(next){ id++; next >>= 1; } printf("%s\n", work[id].na); } int main(){
// freopen("input.txt", "r", stdin); int t, n, i, j; scanf("%d", &t); while(t--){ memset(v, 0, sizeof(v)); scanf("%d", &n); for(i=0; i<n; i++){ scanf("%s%d%d", work[i].na, &work[i].d, &work[i].c); }
dp[0].pre=-1; dp[0].cost=0; dp[0].reduce=0; v[0]=true; for(i=0; i<(1<<n); i++){ //每种状态的遍历 for(j=0; j<n; j++) { //n个工作的遍历 int temp=1<<j; if((i&temp)==0){ //这种状态下j工作还未完成 int next=temp|i ; //该工作完成的状态 int cost=dp[i].cost+work[j].c; //该工作完成后需要的总时间 int reduce= cost-work[j].d; if(reduce<0) {
reduce=0; }
reduce += dp[i].reduce; if(v[next]){ if(reduce < dp[next].reduce){ //下一个状态需要更新 dp[next].reduce = reduce; dp[next].cost = cost; dp[next].pre = i; } } else{ v[next]=true; dp[next].reduce = reduce; dp[next].cost = cost; dp[next].pre = i; } } } printf("%d\n", dp[(1<<n)-1].reduce); print((1<<n)-1);
} } return 0; }

浙公网安备 33010602011771号