HDU 1074 Doing Homework 状态压缩dp

点击打开链接

有n门课,每门课有截止时间和完成所需的时间,如果超过规定时间完成,每超过一天就会扣1分,问怎样安排做作业的顺序才能使得所扣的分最小

第二种方法 
//那么任务所有的状态有2^n-1种
//状态方程为:Dp[next]=min{Dp[k]+i的罚时} 其中,next=k+(1<<i),k要取完满足条件的值 k>>i的奇偶性决定状态k
//具体实现为: 对每种状态遍历n项任务,如果第i项没有完成,则计算出Dp[next]的最优解

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
struct node
{
    char name[110];
    int dealine,spend;
}a[20];
struct NN
{
    int time,score,pre,now;
}dp[1<<15+1];
int main()
{
    int n,T;
    while(~scanf("%d",&T))
    {
        while(T--)
        {
            scanf("%d",&n);
            for(int i=0; i<n; i++)
                scanf("%s%d%d",a[i].name,&a[i].dealine,&a[i].spend);

            int sumstate=1<<n;
            memset(dp,0,sizeof(dp));

            for(int i=1;i<=sumstate;i++)
                dp[i].score=INF;

           /// dp[0].pre=-1;

            for(int i=0; i<sumstate-1; i++)
            {
                
                //for(int j=n-1; j>=0; j--)  ///都可以
                for(int j=0;j<n;j++)
                {
                    int state=1<<j;
                    if((state&i)==0) ///表示state状态中没有第i个项目
                    {
                        int next=state+i;
                        int st=dp[i].time+a[j].spend-a[j].dealine;
                        if(st<0)
                            st=0;
                        if(dp[next].score>st+dp[i].score)
                        {
                            dp[next].score=st+dp[i].score;
                            dp[next].time=dp[i].time+a[j].spend;     ///不能用st
                            dp[next].pre=i;
                            dp[next].now=j;
                        }


                    }
                }
            }
            stack<int>Q;
            printf("%d\n",dp[sumstate-1].score);
            int state=sumstate-1;

            while(state)
            {
                Q.push(dp[state].now);
                state=dp[state].pre;
            }
            while(!Q.empty())
            {
                printf("%s\n",a[Q.top()].name);
                Q.pop();
            }
        }
    }
    return 0;
}



///状态方程为:Dp[next]=min{Dp[next-1<<i]+i的罚时} 枚举i与next
///具体实现为: 对每种状态遍历n项任务,如果第i项没有完成,则计算出Dp[next]的最优解


#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
struct node
{
    int dealine,spent;
    char name[110];
}a[20];
struct N
{
    int time,score,pre,now;
}dp[1<<15+1];
int main()
{
    int n,T;
    while(~scanf("%d",&T))
    {
        while(T--)
        {
            scanf("%d",&n);
            for(int i=0;i<n;i++)
                scanf("%s%d%d",a[i].name,&a[i].dealine,&a[i].spent);
            int sumstate=1<<n;
            memset(dp,0,sizeof(dp));
            for(int i=1;i<sumstate;i++)  ///此处i枚举的是后面的状态
            {
                dp[i].score=INF;       ///此时 i = i-state+ state; 
                for(int j=0;j<n;j++)
                {
                    int state=1<<j;
                    if(state&i)
                    {
                        int st=dp[i-state].time+a[j].spent-a[j].dealine;
                        if(st<0)st=0;  ///如果在期限内就不要减分了
                        if(dp[i].score>dp[i-state].score+st)
                        {
                            dp[i].score=dp[i-state].score+st;
                            dp[i].time=dp[i-state].time+a[j].spent;
                            dp[i].now=j;
                            dp[i].pre=i-state;
                        }
                        else if(dp[i].score==dp[i-state].score+st)///此处的分数相同时 , 表示的字符串的名字按字典序输出
                        {
                            if(dp[i].now<j)///i表示后面的状态 所以此时应该让他的字典序尽可能的大,才能保证前面的尽可能小
                            {
                              dp[i].score=dp[i-state].score+st;
                              dp[i].time=dp[i-state].time+a[j].spent;
                              dp[i].now=j;
                              dp[i].pre=i-state;
                            }
                        }
                    }
                }
            }
            stack<int>Q;
            int next=sumstate-1;
            printf("%d\n",dp[next].score);
            while(next)
            {
                Q.push(dp[next].now);
                next=dp[next].pre;
            }
           // cout<<Q.size();
            while(!Q.empty())
            {
                printf("%s\n",a[Q.top()].name);
                Q.pop();
            }
        }
    }
    return 0;
}


也可以这么写
   for(int i=1;i<sumstate;i++)
            {
                dp[i].score=INF;
                for(int j=n-1;j>=0;j--) 
                
    ///i表示后面的状态 所以此时应该让他的字典序尽可能的大,才能保证前面的尽可能小, 所以按照字典序大的先枚举
                {
                    int state=1<<j;
                    if(state&i&&i>=state)
                    {
                        int st=dp[i-state].time+a[j].spent-a[j].dealine;
                        if(st<0)st=0;
                        if(dp[i].score>dp[i-state].score+st)
                        {
                            dp[i].score=dp[i-state].score+st;
                            dp[i].time=dp[i-state].time+a[j].spent;
                            dp[i].now=j;
                            dp[i].pre=i-state;
                        }
                    }
                }
            }





<span id="transmark"></span>


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值