hdu_1074

这道题不难,但是做出来了还是觉得很有成就感,首先是以前不会状态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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值