HDU - 1074 Doing Homework(状压DP)

状压DP

题意: 有n门作业,每门作业都有最晚完成时间以及完成该作业需要的天数, 过了最晚完成时间后一天减一分,问最后最少减多

少分就可以完成所有作业以及输出做作业的顺序

思路:因为最多有15门作业,所以考虑状态压缩, 做n门作业时可以由做n-1门作业时再做一门转化而来;

递推方程:dp[i|(1 << j)] = min(dp[i] + ju, dp[i|(1 << j)]); i为状态的遍历, 做一门作业时把该位设为1, 等于不做这一门时的分数加

上再做这一门时应该加上的分数, ju = 做i门需要的时间 + 第j门时间 - 第j门完成时间, 小于0时为0, 不会增加分数;

更新时用path记下更新的路径, 最后通过递归倒序寻找做作业的顺序,输出即可;

代码如下:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
struct P
{
    char s[105];
    int d, u;
}t[20];
int path[1 << 15];
int dp[1 << 15];
int n;
int T;
void print(int k)
{
    for(int i = 0; i < n; i++)
    {
        if((k&(1 << i)) != 0 && path[k] == k - (1<<i))
        {
            //printf("-------------------\n");
            print(path[k]);
            printf("%s\n", t[i].s);
        }
    }
}
int main()
{
    cin >> T;
    int time;
    int ju;
    while(T--)
    {
        memset(dp, INF, sizeof(dp));
        memset(path, -1, sizeof(path));
        scanf("%d", &n);
        for(int i = 0; i < n; i++)
            scanf("%s%d%d", t[i].s, &t[i].d, &t[i].u);
        dp[0] = 0;
        for(int i = 0; i < (1 << n); i++)
        {
            time = 0;
            for(int j = 0; j < n; j++)
            {
                if(i & (1<<j))
                {
                    time += t[j].u;
                }
            }
            for(int j = 0; j < n; j++)
            {
                if((i & (1 << j)) == 0)
                {
                    if(time + t[j].u < t[j].d)
                        ju = 0;
                    else
                        ju = time + t[j].u - t[j].d;
                    if(dp[i] + ju < dp[i|(1<<j)])
                    {
                        dp[i|(1<<j)] = dp[i] + ju;
                        path[i|(1<<j)] = i;
                    }
                    //dp[i|(1<<j)] = min(dp[i|(1<<j)], dp[i] + ju);
                }
            }
        }
        //printf("%d\n", dp[1]);
        printf("%d\n", dp[(1 << n) - 1]);
        print((1 << n) - 1);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值