HDOJ 1074 Doing Homework (状压DP)

本文介绍了一种使用状态压缩动态规划(状压DP)的方法来解决一项关于作业安排的问题,该问题需要找到完成所有作业的最优方案,使得因延迟提交而被扣除的分数最少。文中提供了一个详细的代码实现过程。
题意:给出 n 种作业,然后给出每种作业交作业的截至日期,和完成这项任务需要的时间。如果在截至日期后交作业,那么每晚一天就要扣一分。求出扣分最少为多少,并且打印最小扣分完成作业的方案,n <=15。

看一下 n,很小只有15,最开始想的就是暴力打印完成这 15 项作业的所有方案,那么就是 15! ,就明显T了。
那么考虑一下DP,开一个数组,dp[i] ,表示完成 i 种课程的最小扣分,但是还是想不到怎么转移,对于 i 种课程有很多种可能,那么就再加一维,dp[i][j],表示的是完成 i 种课程,并且这 i 种课程分别是 j的二进制。那么就按照状压DP的思路写起走就行了。分别枚举 i ,j,然后再来转移。
然后再来考虑一下打印路径的问题,对于每一个dp[i][j],纪录转移到这个状态的前一个 j ,和新加入的课程,也就是两个前驱,再打印就可以了。
题目要求是要按照字典序来打印,这里没有管字典序的问题也能过,想不通。

#include<stdio.h>
#include<algorithm>
#include<string>
#include<string.h>
#include<queue>
#include<vector>
#include<stack>
#include<math.h>
#include<map>
#include<iostream>
using namespace std;
#define maxn 100005
struct node
{
    int use;
    int time;
    int preclass;
    int prej;
}dp[16][1<<16];
struct nnode
{
    char name[110];
    int dead;
    int day;
}a[20];
int n,mask;
vector<int> s[20];
char ans[20][110];
int solve(int x)
{
    int cnt=0;
    int dx=x;
    while(dx)
    {
        if(dx%2==1)
            cnt++;
        dx/=2;
    }
    return cnt;
}
int main()
{
    int t;
    for(int i=0;i<(1<<15);i++)
    {
        int cnt=solve(i);
        s[cnt].push_back(i);
    }
    scanf("%d",&t);
    while(t--)
    {
        memset(dp,0x7f,sizeof(dp));
        scanf("%d",&n);
        mask=(1<<n)-1;
        for(int i=0;i<n;i++)
            scanf("%s%d%d",a[i].name,&a[i].dead,&a[i].day);
        for(int i=0;i<=mask;i++)
            dp[0][i].time=dp[0][i].use=0;
        for(int i=1;i<=n;i++)
        {
            int len=s[i-1].size();
            for(int jj=0;jj<len;jj++)
            {
                int j=s[i-1][jj];
                for(int k=0;k<n;k++)
                {
                    int tmp=(1<<k);
                    if((j&tmp)==0)
                    {
                        int now=(j|tmp);
                        int tmptime=dp[i-1][j].time+a[k].day-a[k].dead;
                        if(tmptime<=0)
                            tmptime=0;
                        tmptime+=dp[i-1][j].use;
                        if(dp[i][now].use>tmptime)
                        {
                            dp[i][now].use=tmptime;
                            dp[i][now].time=dp[i-1][j].time+a[k].day;
                            dp[i][now].prej=j;
                            dp[i][now].preclass=k;
                        }
                    }
                }
            }
        }
        printf("%d\n",dp[n][mask].use);
        int now=mask;
        int cnt=0;
        for(int i=n;i>=1;i--)
        {
            strcpy(ans[i],a[dp[i][now].preclass].name);
            now=dp[i][now].prej;
        }
        for(int i=1;i<=n;i++)
            printf("%s\n",ans[i]);
    }
return 0;
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值