这道题不难,但是做出来了还是觉得很有成就感,首先是以前不会状态dp,第二是这几天温度变化太大,自己状态不怎么好。。。唉,看图论看的快疯了。。早上起来发现做的梦都是关于图论的。。啊!!图论坑爹啊。。T T。。学了一下之后才知道,原来状态dp利用的思想就是离散数学里格的思想,他本质上就是一个n阶的布尔代数,拥有一切布尔代数的性质,所有的状态可以唯一表示为n个原子的集合,这也是证明这种dp正确性必不可少的条件。
正常的思路是按照n阶布尔代数hash图的的顺序,从最低层往上层递推状态,可是这样编程难度太高,所以不妨采用另外一种思想,Boolean Algebra有一个性质,如果这个布尔代数是n阶的,那么任何小于n阶的布尔代数都是这个代数的子代数,所以,我们可以把一个k-1阶的子boolean代数的状态转移到k阶的子boolean代数,这也就形成了一个自然序(0.1.2.....n),然后也就有了如下的代码。
#include<iostream>
#include<vector>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=16;
const int inf = 0x7fffffff;
struct ww
{
string name;
int endt;
int needt;
}work[maxn];
struct pp
{
int pre;
int reduce;
int spend;
int id;
}dp[1<<maxn];
int T,N,temp,sum,add;
stack<int>stacks;
void init()
{
dp[0].reduce=0;
dp[0].pre=0;
dp[0].spend=0;
for(int i=1;i<(1<<N);i++)
{
dp[i].pre=0;
dp[i].reduce=inf;
dp[i].spend=0;
}
}
void dpstart()
{
int dpend=1<<N;
for(int now=1;now < dpend ;now++)
{
for(int i=N;i>=1;i--)
{
if( now & 1<<(i-1) )
{
temp=now-(1<<(i-1));
sum=0;
for(int j=1;j<=N;j++)
{
if(! ( temp & ( 1<< (j-1) ) ) )
{
if(dp[temp].spend >= work[j].endt )
{
sum+=work[i].needt;
}
else if(dp[temp].spend + work[i].needt > work[j].endt )
{
sum+=dp[temp].spend + work[i].needt - work[j].endt;
}
}
}
sum+=dp[temp].reduce;
if(sum < dp[now].reduce )
{
dp[now].reduce=sum;
dp[now].pre=temp;
dp[now].spend=dp[temp].spend + work[i].needt;
dp[now].id=i;
}
}
}
}
}
int main()
{
cin>>T;
for(int tt=1;tt<=T;tt++)
{
cin>>N;
for(int i=1;i<=N;i++)
{
cin>>work[i].name>>work[i].endt>>work[i].needt;
}
init();
dpstart();
cout<<dp[(1<<N)-1].reduce<<endl;
while(!stacks.empty())
{
stacks.pop();
}
stacks.push((1<<N)-1);
while(dp[stacks.top()].pre)
{
stacks.push(dp[stacks.top()].pre);
}
while(!stacks.empty())
{
temp=stacks.top();
stacks.pop();
cout<<work[dp[temp].id].name<<endl;
}
}
return 0;
}