【HDU1074 dp狀態壓縮】Doing Homework 第一道狀態壓縮dp

Doing Homework

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 5197    Accepted Submission(s): 2160


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.
 

Author
Ignatius.L


雖然是看的別人代碼,但是思想還是有所感覺,還需要做幾個才能明白

開始我想和其他dp一樣找狀態轉移方程,但感覺他的狀態要表示出來很困難。由於題目說作業數最多是15,因此若我們採用一個2的15次方的數來表示,每一個數爲0則表示尚未完成此項作業,爲1則表示已完成此項作業,這樣這個狀態就很好表示了。

在這裏設置一個結構體,裏面要包含一個記錄前驅的pre,然後自然的有最小的扣分代價,此外,由於是否在規定時間內完成了這道題目是由當前時間,截止時間和你花費時間共同決定的,那你就必須判斷當前時間與截止時間的差距是多少,因此外設一個表示當前時間。

以dp[i]來表示完成了i這個二進制數中所有爲1的位置的對應的作業的狀態(感覺有點繞。。。)

至於遞推條件:(1)對當前狀態i進行枚舉他所爲0的部分,j,若(i&j==0)則說明j這樣作業尚未被完成。(2)然後將其添加,變成cell=i|j,在更新dp[cell]。在此我們要設一個標記數組,因爲我們並不知道之前cell是否已經被更新。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxsize 1<<15
#define inf 1<<30
struct dp
{
    int mincost;   //最小的損失
    int pre;  //指向對應的前驅
    int nowtime;//當前時間的進程
} dp[maxsize]; //這個,是作爲當前形成這種狀態的一個對應數組
int visited[maxsize];
struct node
{
    int deadline;
    int cost;
    char name[101];
};//這個是針對於課程的
node course[15];
int n;
void print_(int k, int t)
{
    int v, cnt = 0;
    v = (k ^ t); //這裏採用異或操作找出對應的位置
    while (v)
    {
        v = v >> 1;
        cnt++;
    }
    k = t;
    t = dp[t].pre;
    if (t != -1)print_(k, t);
    printf("%s\n", course[cnt - 1].name);
}
int main()
{
    int icase, i, j;
    scanf("%d", &icase);
    while (icase--)
    {
        scanf("%d", &n);
        for (i = 0; i < n; i++)
            scanf("%s %d%d", course[i].name, &course[i].deadline, &course[i].cost);
        int bnumb = 1 << n; //二進制表示的一個總體狀態的數字
        dp[0].nowtime = 0; //根據我上面的定義,自然第0門課程被收入的最小代價爲0;
        dp[0].mincost = 0;
        dp[0].pre = -1;
        for (i = 1; i <= n; i++) dp[i].mincost = inf;
        memset(visited, 0, sizeof(visited)); //初始化標記數組
        //下面開始遍歷進行找出最小的代價
        for (i = 0; i < bnumb - 1; i++) //爲什麼要是bnumb-1是因爲2的n次方減一纔是他正常的二進制表示形式,n位1嘛。
        {
            for (j = 0; j < n; j++)
            {
                int cell, currt, subt, allsu;
                int temp = 1 << j;
                if ((temp & i) == 0)
                {
                    //這種情況說明了當前這個j不在我們完成的課程裏
                    cell = temp | i; //把這個j加入到了當前情況中
                    currt = dp[i].nowtime + course[j].cost; //把當前dp所走到的時間再加上要完成此課會消耗的時間
                    subt = currt - course[j].deadline; //求出此時此課的最小代價
                    if (subt < 0) subt = 0;
                    allsu = dp[i].mincost + subt;
                    if (visited[cell]) //如果被訪問過了
                    {
                        if (allsu < dp[cell].mincost)
                        {
                            dp[cell].mincost = allsu;
                            dp[cell].pre = i;
                            dp[cell].nowtime = currt;
                            //printf("i:%d,cell:%d\n",i,cell);
                            //printf("%d %d %d\n",dp[cell].mincost,dp[cell].pre=i,dp[cell].nowtime);
                        }//對於相應的節點進行修改
                    }
                    else if (!visited[cell])
                    {
                        dp[cell].mincost = allsu;
                        dp[cell].pre = i;
                        dp[cell].nowtime = currt;
                        visited[cell] = 1;
                        //printf("i:%d,cell:%d\n",i,cell);
                        //printf("%d %d %d\n",dp[cell].mincost,dp[cell].pre=i,dp[cell].nowtime);
                    }
                }
            }
        }//以上是把最小的那個帶價值求出來了dp[1<<n-1].mincost;
        //由於代碼中是隻有當allsu<dp[cell].mincost才更新,因此按輸入順序必然是輸出最小的那一個起
        printf("%d\n", dp[(1 << n) - 1].mincost);
        int k = (1 << n) - 1;
        int t = dp[(1 << n) - 1].pre;
        print_(k, t);
    }
    return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章