HDU5550 - Game room (DP)

Keywords: PHP github

Topic link:

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

Main idea of the title:

There is a building, there are N floors, each floor has ai want to play A games, bi want to play B games, but each floor can only build a game hall. It takes a little physical effort for everyone to move up and down the floor. Make everyone play the game and consume as little physical energy as possible, the least physical expenditure.

Problem solving process:

The first idea is greedy, sweeping down from the 0th floor, accumulating the number of people who want to play A games and the number of people who want to play B games. For each level, we can judge whether there are more people who want to play A or B, and build the one with more people.

Obviously, this idea can not get the optimal solution. If everyone can only go down but not up, it should be feasible.

Then this topic has been placed for a long time, and now I have to fill it. I can read two or three blogs. I feel that this kind of DP can only rely on brain holes, and each one is different.

Reference Blog:
https://ramay7.github.io/2016/11/04/HDU-5550-2015CCPC-K-dp/

http://blog.csdn.net/snowy_smile/article/details/49618219

Topic analysis:

The above two blogs are very detailed, I mainly refer to the first blog, the code has been annotated, here do not discuss too much.

AC code:

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAX_N = 4010;
const ll INF = 0x3f3f3f3f3f3f3f3fll;

int T, n, cases = 0;
ll value[MAX_N][2], sum[MAX_N][2], pre[MAX_N][2], suf[MAX_N][2];
ll dp[MAX_N][2];

void init() {
    //Initialize the prefix sum, pre[i] represents the cost of moving from layer I to layer 0, and suf[i] represents the cost of moving from layer I to layer n+1.
    for (int i = 1; i <= n; i++) {
        sum[i][0] = sum[i-1][0] + value[i][0];
        sum[i][1] = sum[i-1][1] + value[i][1];
        pre[i][0] = pre[i-1][0] + value[i][0] * i;
        pre[i][1] = pre[i-1][1] + value[i][1] * i;
    }
    for (int i = n; i >= 1; i--) {
        suf[i][0] = suf[i+1][0] + value[i][0] * (n - i + 1);
        suf[i][1] = suf[i+1][1] + value[i][1] * (n - i + 1);
    }
}

ll down(int a, int b, int id) {
    //Indicates the cost of achieving b+1 for people in [a, b] interval
    return suf[a][id] - suf[b+1][id] - (sum[b][id] - sum[a-1][id]) * (n-b);
}

ll up(int a, int b, int id) {
    //Indicates the cost of reaching a for a person in the [a,b] interval.
    return pre[b][id] - pre[a][id] - (sum[b][id] - sum[a][id]) * a;
}

ll work(int a, int b, int id) {
    int mid = (a+b) >> 1;
    //Because dp[i][0] represents the current state of 0, i+1 is 1, if the current I is n, then there is no room of 1 behind.
    if (b < n) return up(a, mid, id) + down(mid+1, b, id);
    else return up(a, b, id);
}

void solve() {
    init();
    for (int i = 1; i <= n; i++) {
        dp[i][0] = dp[i][1] = INF;
        //The case where the first i are all 1 or all 0
        if (i < n) dp[i][0] = down(1, i, 1);
        if (i < n) dp[i][1] = down(1, i, 0);
        for (int j = 1; j <= i-1; j++) {
            //Enumerate the position of one or 0 selected above
            dp[i][0] = min(dp[i][0], dp[j][1] + work(j, i, 1));
            dp[i][1] = min(dp[i][1], dp[j][0] + work(j, i, 0));
        }
    }
    printf("Case #%d: %lld\n", ++cases, min(dp[n][0], dp[n][1]));
}

int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%lld%lld", &value[i][0], &value[i][1]);
        }
        solve();
    }
}

Posted by Haloscope on Mon, 11 Feb 2019 04:45:18 -0800