hdu 1789 Doing Homework again【贪心 || 贪心+优先队列】

链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1789

http://acm.hust.edu.cn/vjudge/contest/view.action?cid=29256#problem/A

Doing Homework again

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4618    Accepted Submission(s): 2707


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. And now we assume that doing everyone homework always takes one day. 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 that is the number of test cases. T test cases follow.
Each test case start with a positive integer N(1<=N<=1000) which indicate the number of homework.. Then 2 lines follow. The first line contains N integers that indicate the deadlines of the subjects, and the next line contains N integers that indicate the reduced scores.
 

Output
For each test case, you should output the smallest total reduced score, one line per test case.
 

Sample Input
 
    
3 3 3 3 3 10 5 1 3 1 3 1 6 2 3 7 1 4 6 4 2 4 3 3 2 1 7 6 5 4
 

Sample Output
 
    
0 3 5
 

Author
lcy
 

Source
2007省赛集训队练习赛(10)_以此感谢DOOMIII
 

Recommend
lcy
 


算法:贪心


思路:


本来应该是用 DP 解了, 毕竟贪心只能解决局部最优,而 DP 解决的是全局最优,就像浩神说的一样,一切问题都可以转换为DP
但是 DP 实在是太弱了,这题数据比较小贪心两次循环就可以解决。。。

先对作业排序:
作业先截止的先写, 截止日期相同的先写扣分多的
所以确定了如何排序。

开始扫描天数,每天只能完成一门作业,当然先做截止日期内的,扣分最多的,前面我们已经排序好了。
但是这样排序后只保证了局部最优,如果后面出现了一个扣分很多,但是到截止日期没有完成的怎么办?【也就是局部最优转换为全局最优问题】
那么我们可以再建立一层循环,往前面找完成了的作业,但是扣分比当前不能按期完成的作业扣分少的,
从找出的这堆作业中再找一个扣分最少的,用它的完成时间来完成当前不能完成的作业。

时间复杂度 O(n*n) 

如果用 DP 应该可以降到 O(n*logn)
但是刚刚 10 级的黄超学长提了下:  贪心很多时候可以考虑最大最小堆(用优先队列实现) n*logn 那么就和 DP 一样了
没有用过最大堆,最小堆的怎么破 Orz

PS: 思路好像写的乱了点,可能会纠结不清楚,表示还是直接看代码比较好。。。

code:

Accepted 1789 62MS 284K 2146 B C++ free斩

//贪心解DP
#include
#include
#include
using namespace std;

const int maxn = 1000+10;

struct Node{
    int day; //截止日期
    int score; //没有及时交作业,所扣的分
    int flag; //标记作业是否写
}node[maxn];

bool cmp(Node a, Node b)
{
    if(a.day == b.day) return a.score > b.score; //同一天截止,先写扣分多的
    else return a.day < b.day; //先写提前截止的,暂时保证局部最优
}

int main()
{
    int T;
    int n;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n); //作业数
        for(int i = 0; i < n; i++)
            scanf("%d", &node[i].day);
        for(int i = 0; i < n; i++)
            scanf("%d", &node[i].score);
        for(int i = 0; i < n; i++)
            node[i].flag = -1; //初始化标记,都没有做
        sort(node,node+n,cmp);

        int day = 1; //从第一天开始遍历
        int Index;
        int Min;
        for(int i = 0; i < n; i++)
        {
            if(day <= node[i].day) //能完成,直接写,先保证局部最优
            {
                node[i].flag = 1; //标记完成了
                day++; //天数加一
            }
            else //如果到截止日期不能完成,就往前面的天数找,替换前面写了的,但是扣分最少的
            {
                Min = node[i].score;
                Index = -1; //标记找不到
                for(int j = i-1; j >= 0; j--) //往前面遍历, 同时更新最小扣分
                {
                    if(node[j].flag == 1 && node[j].score < Min) //如果找到
                    {
                        Min = node[j].score; //更新最小扣分
                        Index = j; //记录找到的下标
                    }
                }

                if(Index >= 0) //如果找到
                {
                    node[Index].flag = -1; //替换,前面的标记为未完成
                    node[i].flag = 1; //当前标记为完成
                }
            }
        }

        int ans = 0;
        for(int i = 0; i < n; i++) //依次遍历,找出为完成的作业
        {
            if(node[i].flag == -1)
                ans += node[i].score;
        }
        printf("%d\n", ans);
    }
    return 0;
}


后来按照这种思路排序的又用优先队列试了下,时间减少了一半:
Accepted 1789 31MS 256K 1783 B C++ free斩

#include
#include
#include
#include
using namespace std;

const int maxn = 1000+10;

struct Node{
    int day;
    int score;
    bool operator <(const Node &b) const{ //按照分数由小到大排序
         return b.score < score; //注意不要忘记return...
    }
}node[maxn];

bool cmp(Node a, Node b)
{
    if(a.day == b.day) return a.score > b.score;
    else return a.day < b.day;
}

int main()
{
    int T;
    int n;
    scanf("%d", &T);
    while(T--)
    {
        int sum = 0;
        scanf("%d", &n);
        for(int i = 0; i < n; i++)
            scanf("%d", &node[i].day);
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &node[i].score);
            sum += node[i].score;
        }

        sort(node, node+n, cmp);

        priority_queue q;
        while(!q.empty()) q.pop();

        int index = 1;
        for(int i = 0; i < n; i++)
        {
            if(index <= node[i].day)
            {
                q.push(node[i]);
                index++;
            }
            else
            {
                Node tmp = q.top();
                if(tmp.score < node[i].score) //如果队列中最小的分数要比当前要完成的分数小,则时间替换
                {
                    q.pop();
                    q.push(node[i]);
                }
            }
        }
        while(!q.empty()) //总分数 - 已经完成了的作业的分数
        {
            sum -= (q.top()).score;
            q.pop();
        }
        printf("%d\n", sum);

    }
    return 0;
}

队友ORC直接用的贪心,不过贪的方法不同,没有用优先队列效率也比较高:
9002782 2013-08-19 13:05:55 Accepted 1789 31MS 216K 987B G++ CSUST_2012_16

#include
#include
#include
using namespace std;
const int maxn=1010;
struct point
{
    int s,d;
}p[maxn];
int cmp(point a,point b)
{
    if(a.s==b.s) return a.d>b.d;
    return a.s>b.s;
}
int main()
{
    int i,j,k,n,m;
    int vis[maxn];
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        memset(p,0,sizeof(p));
        memset(vis,0,sizeof(vis));
        for(i=1;i<=n;i++)
            scanf("%d",&p[i].d);
        for(i=1;i<=n;i++)
            scanf("%d",&p[i].s);
        sort(p+1,p+n+1,cmp);
        int ans=0;
        for(i=1;i<=n;i++)
        {
            for(j=p[i].d;j>=1;j--)//从后面开始完成,遮掩前面就可以腾出更多的空间个别的作业
            {
                if(!vis[j]) //如果第 j 天没有被占用
                {
                    vis[j]=1; //用第 j 天写第 i 个作业
                    break;
                }
            }
            if(j==0) ans+=p[i].s; //如果没法完成第 i 个作业
        }
        printf("%d\n",ans);
    }
    return 0;
}



你可能感兴趣的:(acm,解题报告,hdu,贪心,算法,c++,贪心)