HDU-1074-Doing Homework

Problem Description
Ignatius has just come back school from the 30th ACM/ICPC. Now he has a lot of homework to do. Every teacher gives him a deadline of handing in the homework. If Ignatius hands in the homework after the deadline, the teacher will reduce his score of the final test, 1 day for 1 point. And as you know, doing homework always takes a long time. So Ignatius wants you to help him to arrange the order of doing homework to minimize the reduced score.


Input
The input contains several test cases. The first line of the input is a single integer T which is the number of test cases. T test cases follow.
Each test case start with a positive integer N(1<=N<=15) which indicate the number of homework. Then N lines follow. Each line contains a string S(the subject's name, each string will at most has 100 characters) and two integers D(the deadline of the subject), C(how many days will it take Ignatius to finish this subject's homework).

Note: All the subject names are given in the alphabet increasing order. So you may process the problem much easier.


Output
For each test case, you should output the smallest total reduced score, then give out the order of the subjects, one subject in a line. If there are more than one orders, you should output the alphabet smallest one.


Sample Input
2
3
Computer 3 3
English 20 1
Math 3 2
3
Computer 3 3
English 6 3
Math 6 3


Sample Output
2
Computer
Math
English
3
Computer
English
Math

Hint

In the second test case, both Computer->English->Math and Computer->Math->English leads to reduce 3 points, but the

word "English" appears earlier than the word "Math", so we choose the first order. That is so-called alphabet order.

-----------------------------------------------------------------------------一点也不华丽的分界线-----------------------------------------------------------------------------

这道题是一道典型的状态压缩型dp。

拿到这道题我们首先想的是,怎么解能够把这个题目给解出来。可能因为看刘汝佳的《算法竞赛入门经典入门指南》的时候有道题目影响了我,让我觉得这道题对deadline排下序就可以轻松的解决了。这也是我们的直觉。这样做的话就是一道贪心问题了。但是我们思考一个问题,两个work完成顺序的先后对结果有没有影响?那必然是有的,因为还有一个输出顺序的问题(也许不考虑输出顺序的时候就能够直接排序解决博主未证明这个算法)。例如Computer 20 1,English 5 3, Math 5 1, 输出结果必然是CEM才对,但是一旦排序,那么结果就变成了EMC。既然两个work顺序对结果有影响,那么我们就不能使用贪心来解决这个问题。那么要怎么解决这个问题呢?

我们思考,n个课程,按照deadline排序的结果必然不对,按照handle time排序肯定更加不正确了。那么一定有一个顺序能够解决这个问题,但是n个课程,有n!个顺序,而n!是最大为15!很明显,这种算法可以直接忽略,O(n!)的算法基本上已经可以是最差的算法了,在有更优解法的时候,一定不能采取这个算法。这个时候想到,无论什么顺序我只需要得到n个课程都完成的时候的结果,那么n个课程都完成的状态的上一个状态是什么呢?就是有一门课程没有完成时候的状态,再上一个状态呢?我们发现,按照这个思路,n个课程只有完成和未完成两种状态而且n<=15那么可以直接使用二进制的01表示完成未完成。那么对于第i个课程来说,最优解的状态来自于 i - (1 << j)的状态,j表示第j门课程。这样就得出来一个状态转移方程dp[i] = dp[i - (1 << j)] + cost;得到这个方程之后,就可以非常轻松的撸出来这道题了。当然,由于这道题需要按照字典序输出解决课程的顺序,所以我们需要用一个数组来表示第i个状态的前一个状态。当然可能会有读者问,这样子为什么可以输出字典序的结果?因为输入保证了输入课程是按照字典序来输出,而我们选取课程时是按照逆序来选取,也就是说,如果有两门课程顺序颠倒不影响最小cost,那么字典序靠后的一定会被先记录下来,然后后输出。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f

const int maxn = 17;
typedef struct node{
    char name[105];
    int d, c;
}HW;

HW hw[maxn];
int dp[1 << 17], sum[1 << 17], pre[1 << 17];

bool cmp(HW a, HW b){
    return strcmp(a.name, b.name) >= 0;
}
inline int Min(int x, int y){
    return ( x < y ? x : y );
}
inline int Max(int x, int y){
    return ( x > y ? x : y );
}
void Print(int st){
    if(st == 0) return;
    Print(pre[st]);

    int j = 0, tmp = st ^ pre[st];
    while(tmp){
        tmp >>= 1;
        ++j;
    }
    printf("%s\n", hw[j - 1].name);
}
int main()
{
    // freopen("test.in", "r+", stdin);
    // freopen("test.out", "w+", stdout);

    int n, t;
    scanf("%d", &t);
    while(t--){
        scanf("%d", &n);
        memset(hw, 0, sizeof(hw));
        memset(pre, 0, sizeof(pre));
        memset(sum, 0, sizeof(sum));
        for(int i = 0; i < n; ++i){
            scanf(" %s%d%d", hw[i].name, &hw[i].d, &hw[i].c);
        }
        sort(hw, hw + n, cmp);

        int st = (1 << n) - 1;
        for(int i = 1; i <= st; ++i) dp[i] = INF;
        dp[0] = 0;
        for(int i = 1; i <= st; ++i){
            int k;
            for(int j = 0; j < n; ++j){
                int tmp = 1 << j;
                if(i & tmp){
                    int tt = sum[i - tmp] + hw[j].c;
                    if(dp[i] > dp[i - tmp] + Max(tt - hw[j].d, 0)){
                        dp[i] = dp[i - tmp] + Max(tt - hw[j].d, 0);
                        sum[i] = tt;
                        k = i - tmp;
                    }
                }
            }
            pre[i] = k;
        }
        printf("%d\n", dp[st]);
        Print(st);
    }
    return 0;
}

如有哪里疏漏,请各位大牛指正

### 关于HDU - 6609 的题目解析 由于当前未提供具体关于 HDU - 6609 题目的详细描述,以下是基于一般算法竞赛题型可能涉及的内容进行推测和解答。 #### 可能的题目背景 假设该题目属于动态规划类问题(类似于多重背包问题),其核心在于优化资源分配或路径选择。此类问题通常会给出一组物品及其属性(如重量、价值等)以及约束条件(如容量限制)。目标是最优地选取某些物品使得满足特定的目标函数[^2]。 #### 动态转移方程设计 如果此题确实是一个变种的背包问题,则可以采用如下状态定义方法: 设 `dp[i][j]` 表示前 i 种物品,在某种条件下达到 j 值时的最大收益或者最小代价。对于每一种新加入考虑范围内的物体 k ,更新规则可能是这样的形式: ```python for i in range(n): for s in range(V, w[k]-1, -1): dp[s] = max(dp[s], dp[s-w[k]] + v[k]) ``` 这里需要注意边界情况处理以及初始化设置合理值来保证计算准确性。 另外还有一种可能性就是它涉及到组合数学方面知识或者是图论最短路等相关知识点。如果是后者的话那么就需要构建相应的邻接表表示图形结构并通过Dijkstra/Bellman-Ford/Floyd-Warshall等经典算法求解两点间距离等问题了[^4]。 最后按照输出格式要求打印结果字符串"Case #X: Y"[^3]。 #### 示例代码片段 下面展示了一个简单的伪代码框架用于解决上述提到类型的DP问题: ```python def solve(): t=int(input()) res=[] cas=1 while(t>0): n,k=list(map(int,input().split())) # Initialize your data structures here ans=find_min_unhappiness() # Implement function find_min_unhappiness() res.append(f'Case #{cas}: {round(ans)}') cas+=1 t-=1 print("\n".join(res)) solve() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值