Codeforces Round #90 C題(三維遞推DP)

http://www.codeforces.com/contest/119/problem/C

  題意:一個學年有N天,每天必須從M個科目中挑選出一個。每個科目有3個屬性,ai、bi、ci,表示第i個科目的作業量在[ai, bi]中,該科目的難度係數爲ci。安排科目時,必須一天比一天的難度係數大除第一天外,每一天的科目還要安排作業量,除第一天外,每一天的作業量必須符合now = past+k or now = past*k。問能否按要求安排完N天的作業,如果能則輸出總作業量最大的安排方案,輸出格式見Sample。
  這題用遞推思路比較清晰,dp[i][j][k]表示第j個科目作業量爲k的狀態,安排在第i天后的總作業量,但作業量的確定值太大,而作業量的範圍<=100,所以第三維只需要保存作業量最小值的增量即可。
  做法是將科目先按ci排個序、記錄原來的位置,再初始化第一天的安排情況,然後一個循環不斷加深天數,循環裏依次更新能夠轉移的狀態(暴力枚舉),並順便記錄下前驅狀態方便回溯路徑。最後的工作就是找出第n天是否存在作業量爲正的狀態,不存在則沒有合法方案,存在則選出最大值,回溯路徑、打印。
  一開始,我的想法是隻開二維,沒有多開一維空間來記錄當前安排到的天數,結果一直在“保證能安排的天數儘可能多”和“保證當前安排的總作業量最大”之間糾結(缺乏DP的觸覺),後來看了隊友Band的代碼,才恍然大悟,只要多開一維來記錄安排天數的深度,則DP時只需關注總作業量最大就可以了……另外代碼中混用了int 和 __int64,不放心的話可以全部改成__int64 or long long ~~

// 90 MS
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<stack>
using namespace std;
 
struct Data {
    __int64 a, b, c;
    int len, pos;    // len是作業量的最大增量,pos記錄當前科目的原始位置
}o[100];
__int64 dp[110][110][110];
pair<int, __int64> pre[110][110][110];
// 分別記錄前驅狀態的科目、作業量增量
 
int f[110];
int n, m, k;
 
bool cmp(Data a, Data b) { return a.c < b.c; }
 
int main() {
    while(cin>>n>>m>>k) {
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= m; ++i) {
            cin>>o[i].a>>o[i].b>>o[i].c;
            o[i].len = o[i].b - o[i].a;
            o[i].pos = i;
        }
        sort(o+1, o+m+1, cmp);
        for(int i = 1; i <= m; ++i) {
            for(int j = 1; j <= m; ++j)
                if(o[j].pos == i) {
                    f[j] = i; break;
                }
        }
        //------------------------------初始化
        for(int i = 1; i <= m; ++i) {
            for(int j = 0; j <= o[i].len; ++j) {
                dp[1][i][j] = o[i].a + j;
            }
        }
        // 核心DP遞推部分
        for(int dep = 1; dep < n; ++dep) {
        //  當前已經安排了科目的天數
            for(int i = dep+1; i <= m; ++i) {
            // 枚舉有可能更新狀態的科目
                for(int j = 1; j < i; ++j) {
                // 枚舉難度係數更小的科目,嘗試更新第i個科目的狀態
                    if(o[i].c == o[j].c) continue;
                    for(int jj = 0; jj <= o[j].len; ++jj) {
                    // 枚舉第j個科目的作業量爲aj + jj時的狀態
                        if(dp[dep][j][jj] == 0) continue;
                        __int64 t = o[j].a + jj;
                        if(o[i].a <= t+k && t+k <= o[i].b) {
                            __int64 u = t+k - o[i].a;
                            if(dp[dep][j][jj] + o[i].a + u > dp[dep+1][i][u]) {
                                dp[dep+1][i][u] = dp[dep][j][jj] + o[i].a + u;
                                pre[dep+1][i][u].first = j;
                                pre[dep+1][i][u].second = jj;
                            }
                        }
                        // 兩種轉移的情況、記錄路徑
                        if(o[i].a <= t*k && t*k <= o[i].b) {
                            __int64 u = t*k - o[i].a;
                            if(dp[dep][j][jj] + o[i].a + u > dp[dep+1][i][u]) {
                                dp[dep+1][i][u] = dp[dep][j][jj] + o[i].a + u;
                                pre[dep+1][i][u].first = j;
                                pre[dep+1][i][u].second = jj;
                            }
                        }
                    }
                }
            }
        }
        //-------------------------------------找出作業量最大的狀態
        int x, y;
        __int64 Max = 0;
        for(int i = 1; i <= m; ++i) {
            for(int j = 0; j <= o[i].len; ++j) {
                //cout<<dp[n][i][j]<<" ";
                if(dp[n][i][j] > Max) {
                    Max = dp[n][i][j];
                    x = i; y = j;
                }
            }
           //cout<<endl;
        }
        //------------------------------判斷是否有合適方案,回溯輸出
        if(!Max) {
            cout<<"NO"<<endl;
            continue;
        }
        cout<<"YES"<<endl;
        stack< pair<int, __int64> > sta;
        for(int i = n; i >= 1; --i) {
            int t1 = x, t2 = y;
            x = pre[i][t1][t2].first;
            y = pre[i][t1][t2].second;
            if(i == 1)
                sta.push(make_pair(t1, dp[i][t1][t2]));
            else
                sta.push(make_pair(t1, dp[i][t1][t2] - dp[i-1][x][y]));
        }
        while(!sta.empty()) {
            cout<<f[sta.top().first]<<" "<<sta.top().second<<endl;
            sta.pop();
        }
    }
    return 0;
}


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