整容说文库 > 程序代码 > 教育资讯

一道DP的ACM题,大家来思考一下吧

来源:学生作业帮助网 编辑:整容说文库 时间:2020/02/29 17:21:13 程序代码
一道DP的ACM题,大家来思考一下吧程序代码
题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=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.

按做作业所花的时间减去最后期限降序排序,如果值相等再按科目名的第一字节升序排序,然后打印出来,是这个意思吗?
引用 1 楼 haozi8993 的回复:
按做作业所花的时间减去最后期限降序排序,如果值相等再按科目名的第一字节升序排序,然后打印出来,是这个意思吗?


是的,但过了期限之后,第一天的smallest total reduced score加一,第二天加二,第三天。。。。。。。。。。。。。。
这个我觉得很难,也想不出有什么方法来解决。
应该不用DP,贪心就可以了,算出每个homework最晚开始的时间finishTime,找出finishTime最大的1个,
作为最后1个任务,然后相当于把所有deadline > finishTime[0]的任务的deadline向前推了,变为finishTime[0]了, 然后重复上面的方法......
引用 3 楼 litaoye 的回复:
应该不用DP,贪心就可以了,算出每个homework最晚开始的时间finishTime,找出finishTime最大的1个,
作为最后1个任务,然后相当于把所有deadline > finishTime[0]的任务的deadline向前推了,变为finishTime[0]了, 然后重复上面的方法......


这样好像不行吧,毕竟每天扣的分数不一样,继续请教。
//1311853 2009-04-26 19:16:12 Accepted 1074 281MS 524K 1323 B C++ no way 
#include<iostream>
#include<string>
using namespace std;
struct Node
{
    string course;
    int deadline;
    int need;
}infor[16];
int n;
int binary[16] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
int flag[65536];
bool mark[16];
string str[16],outs[16];
void dfs(int days,int cost,int sum,int num)//num表示选择的作业数
{ //days表示写作业的开始日期,cost表示前面的花费,sum记录作业是否完成情况
    int i,temp;
    if(num == n)
    {
        if(flag[sum] == cost)
        {
            flag[sum] = cost;
            for(i=0;i<num;i++)
                outs[i] = str[i];
        }
        return ;
    }
    for(i=1;i<=n;i++)
    {
        if(mark[i] == false)
        {
            mark[i] = true;
            sum += binary[i];//要写第i门作业
           ///////////////////////////////////////////////////
            temp = days + infor[i].need - infor[i].deadline;
            if(temp < 0)
                temp = cost;
            else
                temp += cost;
           //  第i门作业完成后的代价 temp
           //////////////////////////////////////
            if(flag[sum] > temp)
            {
                flag[sum] = temp; //记录状态

                str[num++] = infor[i].course;

                dfs(days + infor[i].need,temp,sum,num);

                num --;
            }
            sum -= binary[i];
            mark[i] = false;
        }
    }
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {        
        int i,st=0;
        cin>>n;
        for(i=1;i<=n;i++)
            cin>>infor[i].course>>infor[i].deadline>>infor[i].need;
        for(i=0;i<65536;i++)
            flag[i] = 1000000;
        memset(mark,false,sizeof(mark));//标记状态
        dfs(0,0,0,0);
        for(i=1;i<=n;i++)
            st += binary[i];  //结果存放在下标为st的flag[st]值中
        cout<<flag[st]<<endl;
        for(i=0;i<n;i++)
            cout<<outs[i]<<endl;
    }
    return 0;
}


这是我在网上找的答案,但我不怎么看得明白,大家也来看一下吧
如果看得明白就解释一下啦,呵呵,先谢谢啦。
不好意思,我太弱了,连题目都没看明白,LZ提醒之后才发现有个1天扣1分的条件。
我又想当然的把这道题理解为最多能在规定时间内完成多少个任务。

刚看见1天扣1分的条件感觉像最小生成树,后来又想了1下,感觉这道题还是可以用贪心解,
今天忙,没时间细说了,大概想法就是按照deadline倒序排,如果有时间上的重复,
就把任务1件1件往前推,一直推到最前面,然后从前向后,在每个deadline之前,
选取用时最短的homework来做,这部分可用优先队列实现,当有完不成的任务时,就将剩余量
放入下一部分deadline的队列里面。如果1个时长为5的任务已经做了3天,那么放入新的队列之后,
时长按照2来算。
引用 6 楼 litaoye 的回复:
不好意思,我太弱了,连题目都没看明白,LZ提醒之后才发现有个1天扣1分的条件。
我又想当然的把这道题理解为最多能在规定时间内完成多少个任务。

刚看见1天扣1分的条件感觉像最小生成树,后来又想了1下,感觉这道题还是可以用贪心解,
今天忙,没时间细说了,大概想法就是按照deadline倒序排,如果有时间上的重复,
就把任务1件1件往前推,一直推到最前面,然后从前向后,在每个deadline之前,
选取用时最短的homework来做,这部分可用优先队列实现,当有完不成的任务时,就将剩余量
放入下一部分deadline的队列里面。如果1个时长为5的任务已经做了3天,那么放入新的队列之后,
时长按照2来算。


这样好像不行吧,我这样写之后,算出的结果和示例的output不同啊。
是吗,这两天有空的话我写一个试试吧,LZ给的程序没有仔细看,怕失去了思考的乐趣。
但看起来效率也是不错的!

引用 7 楼 aaa20090987 的回复:
引用 6 楼 litaoye 的回复:
 不好意思,我太弱了,连题目都没看明白,LZ提醒之后才发现有个1天扣1分的条件。
 我又想当然的把这道题理解为最多能在规定时间内完成多少个任务。

 刚看见1天扣1分的条件感觉像最小生成树,后来又想了1下,感觉这道题还是可以用贪心解,
 今天忙,没时间细说了,大概想法就是按照deadline倒序排,如果有时间上的重复,
 就把任务1件1件往前推,一直推到最前面,然后从前向后,在每个deadline之前,
 选取用时最短的homework来做,这部分可用优先队列实现,当有完不成的任务时,就将剩余量
 放入下一部分deadline的队列里面。如果1个时长为5的任务已经做了3天,那么放入新的队列之后,
 时长按照2来算。


 这样好像不行吧,我这样写之后,算出的结果和示例的output不同啊。
LZ能多提供一些测试数据么?用贪心写了一个,不过代码比较恶心,简直就像是凑数弄出来的,
如果测试通过了,再考虑优化一下代码结构。

目前这两个例子测对了,但顺序可能会稍有不同,我自己试了几种,似乎也是对的,但总觉得有些撞大运的感觉。
5楼的代码是已经AC了的,(当然,这不是我写的), 所以你把测试数据用5楼的代码和你的代码测试一下,看两个结果是否相同,就可以知道你的代码正确与否了。
或者你也可以把你的代码放上来,大家一起讨论一下啦。呵呵。
晚上回家贴吧,现在工作中,手头没有代码!

引用 10 楼 aaa20090987 的回复:
5楼的代码是已经AC了的,(当然,这不是我写的), 所以你把测试数据用5楼的代码和你的代码测试一下,看两个结果是否相同,就可以知道你的代码正确与否了。
 或者你也可以把你的代码放上来,大家一起讨论一下啦。呵呵。
用c#写的贪心解法,感觉还有没想到的特殊情况。代码比较恶心,凑合看吧!不好意思!


using System;
using System.Collections.Generic;
using System.Linq;

namespace CommonLab
{
    class Program
    {
        class HomeWork : IComparable
        {
            //结束日期
            public int Deadline;
            
            //花费的时间
            public int Spend;

            //科目名称
            public string Subject;

            //最晚开始做的时间Deadline - Spend
            public int PreDeadline 
            { get { return Deadline - Spend; } }

            public HomeWork() { }

            public HomeWork(int deadline, int spend, string subject)
            {
                Spend = spend;
                Deadline = deadline;
                Subject = subject;
            }

            public HomeWork Clone()
            {
                HomeWork outer = new HomeWork(Deadline, Spend, Subject);
                return outer;
            }

            public int CompareTo(object obj)
            {
                HomeWork B = obj as HomeWork;

                if (B.Spend != Spend)
                    return Spend.CompareTo(B.Spend);
                else if (B.Deadline != Deadline)
                    return Deadline.CompareTo(B.Deadline);
                else
                    return Subject.CompareTo(B.Subject);
            }
        }

        static void Main(string[] args)
        {
            //将作业科目添加到列表
            List<HomeWork> homeWorkList = new List<HomeWork>();
            Dictionary<string, HomeWork> dict = new Dictionary<string, HomeWork>();
            homeWorkList.Add(new HomeWork(10, 10, "Computer"));
            homeWorkList.Add(new HomeWork(11, 1, "English"));
            homeWorkList.Add(new HomeWork(11, 1, "Math"));
            homeWorkList.Add(new HomeWork(11, 1, "Math1"));
            homeWorkList.Add(new HomeWork(11, 1, "Math2"));
            homeWorkList.Add(new HomeWork(11, 1, "Math3"));
            homeWorkList.Add(new HomeWork(11, 1, "Math4"));

            //按照deadline排序
            homeWorkList.Sort(CompareByDeadline);

            //将列表内容复制一个副本
            for (int i = 0; i < homeWorkList.Count; i++)
                dict.Add(homeWorkList[i].Subject, homeWorkList[i].Clone());

            //从后向前,把deadline向前推,处理后的任务占用时间互不重合
            for (int i = homeWorkList.Count - 2; i >= 0; i--)
            {
                if (homeWorkList[i].Deadline > homeWorkList[i + 1].PreDeadline)
                    homeWorkList[i].Deadline = homeWorkList[i + 1].PreDeadline;
            }

            //建立一个优先队列,花费时间短的任务排在前头
            SortedDictionary<HomeWork,int> pQueue = new SortedDictionary<HomeWork,int>();
            int sumSpend = 0, currentSpend = 0, currendIndex = 0, reduced = 0;
            Queue<string> homeWorkQueue = new Queue<string>();
            int deadline = dict[homeWorkList[0].Subject].Deadline;

            while (true)
            {
                //处理deadline之间的任务,将互相冲突的任务都加入队列
                while (currendIndex < homeWorkList.Count && homeWorkList[currendIndex].Deadline <= deadline)
                {
                    sumSpend += homeWorkList[currendIndex].Spend;
                    pQueue.Add(homeWorkList[currendIndex], homeWorkList[currendIndex].Spend);
                    currendIndex++;
                }

                //尽量选取花费时间短的任务
                while ((currentSpend < deadline || currendIndex >= homeWorkList.Count) && pQueue.Count > 0)
                {
                    HomeWork item = pQueue.ElementAt(0).Key;

                    if (currentSpend + item.Spend <= deadline || currendIndex >= homeWorkList.Count)
                    {
                        homeWorkQueue.Enqueue(item.Subject);
                        pQueue.Remove(item);
                        currentSpend += item.Spend;

                        //计算被扣的分数
                        if (currentSpend > dict[item.Subject].Deadline)
                            reduced += currentSpend - dict[item.Subject].Deadline;
                    }
                    else
                    {
                        //如果某些任务只做了一半,修改剩余需要花费的时间
                        item.Spend = currentSpend + item.Spend - deadline;
                        currentSpend = deadline;
                    }
                }

                //设定下一步需处理的deadline
                if (currendIndex >= homeWorkList.Count)
                    break;
                else if (pQueue.Count > 0)
                    deadline = Math.Min(sumSpend, dict[homeWorkList[currendIndex].Subject].Deadline);
                else
                    deadline = dict[homeWorkList[currendIndex].Subject].Deadline;
            }

            //按照计算结果输出
            Console.WriteLine(reduced);
            while (homeWorkQueue.Count > 0)
                Console.WriteLine(homeWorkQueue.Dequeue());
        }

        static int CompareByDeadline(HomeWork A, HomeWork B)
        {
            return A.Deadline.CompareTo(B.Deadline);
        }
    }
}
行了,我把你的代码改成C++之后,交上去AC了
谢谢你啊。
能AC么?那还真不错!我以为还会有些问题呢!

引用 13 楼 aaa20090987 的回复:
行了,我把你的代码改成C++之后,交上去AC了
 谢谢你啊。
呵呵,Thank you.
程序代码