Codeforces Round #90 C题(三维递推DP)

来源:互联网 发布:民间子平何知章388句 编辑:程序博客网 时间:2024/05/17 08:41

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;}