http://blog.csdn.net/libin56842/article/details/24316493
http://blog.csdn.net/xingyeyongheng/article/details/21742341#comments
首先贴两个状态压缩的题解,这是第一个状态压缩DP,想了一下午,不知道是不是有点慢····
然后在贴下我自己的代码
#include<iostream>
#include<cstdio>
#include<string.h>
#include<string>
#include<set>
#include<algorithm>
#include<cmath>
#include<stack>
#define ll __int64
#define MAX 1000009
using namespace std;
/*
题意:给你n门功课,每门功课的截止时间和需要完成的天数,没超过截止时间一天,失去分数+1,问你如何排列做作业顺序的情况下,失去分数最小
分析:一共有n!情况,假设 n = 3的时候1 2 3,1 3 2,2 1 3,2 3 1,3 1 2,3 2 1 这些完成他们消耗的天数是一定的,只是完成顺序不同,所以扣得分数
也是不一样,这样我们就发现了状态,我们把完成相同的作业数看成一种状态,在该状态下,只是功课排列顺序不同,我们求出扣分最小的那一组
这样,我们就来写下状态压缩DP
dp[s] 表示 s状态下失去的最小分数
我们只需要枚举所有作业的情况就可以把状态求出来
我们怎么表示每个作业的完成情况呢?
二进制!位运算
例如 5 101 代表第一,第三门功课完成
假设功课k,状态i 我们怎么得出dp[i]呢,dp[i]由未完成k的dp[i]推出完成K的dp[i]
状态j = i-(1<<k)来完成k达到,并且j<i,如果从0-2^1那么j一定先算过
*/
struct node
{
string name;//名称
int endd;//截止时间
int use;//消耗时间
} a[50];
struct kode
{
int time;//时间
int score;//分数
int pre;//前驱
int now;//当前节点
} dp[MAX];
int main()
{
int T;
int n;
int i,j;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i = 0; i<n; i++)
cin>>a[i].name>>a[i].endd>>a[i].use;
int bit = 1<<n;
for(i = 1; i<bit; i++) //枚举状态
{
dp[i].score = MAX;//初始化
for(j = n-1; j>=0; j--)
{
int temp = 1<<j;
if(i&temp)//状态i不存在作业j完成则不能通过完成作业j到达状态i
{
int past = i - temp;
int st = dp[past].time + a[j].use - a[j].endd;//i-temp表示没有完成j的那个状态
if(st<0)
st = 0;//完成j被扣分数最小是0
if(dp[i].score>dp[past].score + st)
{
dp[i].score = dp[past].score + st;//当前状态下被扣的分数
dp[i].now = j;//当前节点
dp[i].pre = past;//到达状态i的前驱,为了最后输出完成作业的顺序
dp[i].time = dp[past].time + a[j].use;//到达状态i花费的时间
}
}
}
}
stack<int>S;
int tem = bit - 1;
cout<<dp[tem].score<<endl;
while(tem)//这里不断找前驱节点然后存进stack里
{
S.push(dp[tem].now);
tem = dp[tem].pre;
}
while(!S.empty())
{
cout<<a[S.top()].name<<endl;
S.pop();
}
}
return 0;
}