Gym 100676G Training Camp 很好的一個dp

G. Training Camp
[ Color: Yellow ]
Montaser is planning to train very hard for ACM JCPC 2015; he has prepared a list with n topics
to study in the next n days, one topic every day.
Montaser knows that some topics depend on other topics, so he asked coach Fegla and got a list
of m constraints on the order in which he should study these topics.
Also, coach Fegla told him that when he studies topic x on the kth day (1 ≤ k ≤ n), his level will
increase by k*Wx, where Wx is a weight for topic x, representing how hard it is.
Given the list of topics, the weight of each topic, and the list of constrains, can you tell Montaser
what is the maximum level he can reach in these n days? He is currently at level 0 L.


Input
The first line of input contains one integer T representing the number of test cases (1 ≤ T ≤ 128).
The first line of each test case contains two integers: n and m (1 ≤ n ≤ 18).
The next n lines, each contains the title of one of the topics followed by a space, then an integer
W that represents the weight of this topic (1 ≤ W ≤ 100).
The next m lines are of the form: Topic 1 --> Topic 2, which means that Topic 1 must be studied
before Topic 2.
Titles contain only English letters and spaces (no more than 40 characters).
Test cases are separated by a blank line.


Output
For each test case, print the maximum level that Montaser can reach.


Sample Input 
1
3 2
Implementation 3
Dynamic Programming 10
Greedy 7
Greedy --> Dynamic Programming
Implementation --> Dynamic
Programming

Sample Output
47

題意:

  就是一個人要在n天學完n門課程,每天學習一門,在第i天學習第w[i]門課程他的姿勢水平會增加i * w[i],然後這些課程有先後順序,類似於拓撲排序的順序,比如:a -> b, c -> b, c -> a就表示學習a之前要先學習c,學習b之前要先學習a和c,求他能達到的最高的姿勢水平

我一開始想的還是搜索,只能說我對dp還不是很敏感還有解題不夠老練;

思路:

  輸入比較煩人,考慮到n只有18,很容易想到狀態壓縮,用dp[i]表示狀態爲i的時候能達到的最高姿勢水平,那麼dp[i]的值就可以通過枚舉狀態i的爲1的位來得到當前的值

比如dp[010011(2)] = max(dp[000011(2)] + 3(day) * w[4], dp[010001(2) + 3(day) * w[1], dp[010010(2)] + 3(day) * w[0])來得到。複雜度爲

T*n*logn但是T比較大 滿狀態的話複雜度是128*18*2^18 = 6e8,很容易被卡常。所以做個優化,就是因爲不是沒個狀態都是可以的,因爲要按照拓撲排序的

順序,所以我們在轉移方程的時候把能夠存在的狀態給標記一下,這樣就有很多狀態不是o(n)而是o(1)了。

代碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
using namespace std;
const int MAXN = 24;
int a[MAXN], pre[MAXN], n, dp[1<<19];

/*void dfsDp(int i, int j) { //搜索time limit;可以不用dp那個數組
    if (i == n) return ;
    for (int k = 0; k < n; k++) {
        if (!feel[k]) {
            for(int t = 0; t < nSum[k]; t++)
                feel[nNum[k][t]]--;
            feel[k]++;
            int tmp = j|1<<k;
            dfsDp(i+1, tmp);
            dp[i][j] = max(dp[i][j], dp[i+1][tmp]+(i+1)*a[k]);
            for(int t = 0; t < nSum[k]; t++)
                feel[nNum[k][t]]++;
            feel[k]--;
        }
    }
}*/

int main() {
    int t, m;
    freopen("in.txt", "r", stdin);
    scanf("%d", &t);
    while (t--) {
        map<string, int> mp;
        scanf("%d%d", &n, &m);
        memset(dp, -1, sizeof(dp));
        memset(a, 0, sizeof(a));
        memset(pre, 0, sizeof(pre));
        getchar();
        string s, name;
        for(int k = 0; k < n; k++) {
            int flag = 0;
            getline(cin, s);
            int l = s.size();
            for(int i = 0; i < l; i++) {
                if (!flag && s[i] >= '0' && s[i] <= '9') {
                    flag = i;
                }
                if (flag) a[k] = a[k]*10+s[i]-'0';
            }
            name = s.substr(0, flag-1);
            mp[name] = k;
        }
        while (m--) {
            getline(cin, s);
            int l = s.size(), l1;
            int flag = 0;
            string xx;
            for(int i = 0; i < l; i++) {
                if (s[i] == '-' && !flag) {
                    flag = 1;
                    l1 = i-1;
                }
                if (flag == 1 && s[i] == ' ') {
                    flag = 2;
                    continue;
                }
                if (flag == 2) xx += s[i];
            }
            s = s.substr(0, l1);
            pre[mp[xx]] |= 1<<mp[s];
        }
        /*dp[0] = 0; //這種要800ms;
        for(int j = 1; j < 1<<n; j++) {
            int num = 0;
            for(int i = 0; i < n; i++) {
                num += j>>i&1;
            }
            for(int i = 0; i < n; i++) {
                int xx = j&(~(1<<i));
                if (xx != j && dp[xx] >= 0 && (xx&pre[i]) == pre[i]) {
                    //cout<<dp[j]<<' '<<dp[xx]+a[i]*(num)<<endl;
                    dp[j] = max(dp[j], dp[xx]+a[i]*(num));
                }
            }
        }
        */
        dp[0] = 0;// 這種100ms,以後dp看情況吧,是dp[i+k] = dp[i]+a[k];還是dp[i] = dp[i-k]+a[i];
        for(int j = 0; j < (1<<n)-1; j++) {
            if (dp[j] < 0) continue;
            int num = 0;
            for(int i = 0; i < n; i++) {
                num += j>>i&1;
            }
            for(int i = 0; i < n; i++) {
                int xx = j|((1<<i));
                if (xx != j && (j&pre[i]) == pre[i]) {
                    dp[xx] = max(dp[xx], dp[j]+a[i]*(num+1));
                }
            }
        }
        printf("%d\n", dp[(1<<n)-1]);
    }
}


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