Codeforces Round #611 (Div. 3)

A - Minutes Before the New Year

题意:问现在的时间距离0点0分有多少分钟,提问的时间在0点0分之前,且不是0点0分。

题解:?

void test_case() {
    int h, m;
    scanf("%d%d", &h, &m);
    int ans = 0;
    if(m == 0)
        ans += (24 - h) * 60;
    else {
        ans += (23 - h) * 60;
        ans += 60 - m;
    }
    printf("%d\n", ans);
}

B - Candies Division

题意:把n颗糖分给k个人,可以留下一些糖,要求分配的最大值与最小值的差不超过1(尽可能平均),且比最小值多1的人数量不超过一半,在此基础上尽可能多分配。

题解:先平均分配下整,然后零头加上去。

void test_case() {
    int n, k;
    scanf("%d%d", &n, &k);
    int a = n / k;
    int ans = k * a;
    int surplus = min(k / 2, n - ans);
    ans += surplus;
    printf("%d\n", ans);
}

C - Friends and Gifts

题意:每个人必须恰好给出1颗糖果,恰好得到1颗糖果,p[i]表示他要给谁,0表示他等待分配,输入保证不矛盾。分配每个人要给谁,使得满足题目约束。

题解:这张图就是若干条链和若干个环,环不影响直接去掉,然后链全部首尾相连。

int s[200005];
int p[200005];
int h[200005];
int t[200005];
int cnt;
 
int dfs(int u) {
    if(s[u] != 0)
        return dfs(s[u]);
    else
        return u;
}
 
void test_case() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &p[i]);
        s[p[i]] = i;
    }
    cnt = 0;
    for(int i = 1; i <= n; ++i) {
        if(p[i] == 0) {
            if(s[i] == 0) {
                ++cnt;
                h[cnt] = i;
                t[cnt] = i;
            } else {
                ++cnt;
                h[cnt] = i;
                t[cnt] = dfs(i);
            }
            //printf("h=%d\n",h[cnt]);
            //printf("t=%d\n",t[cnt]);
        }
    }
    int j = 1;
    for(int i = 1; i <= n; ++i) {
        if(p[i] == 0) {
            p[i] = t[++j];
            if(j > cnt)
                p[i] = t[1];
        }
    }
    for(int i = 1; i <= n; ++i)
        printf("%d%c", p[i], " \n"[i == n]);
}

D - Christmas Trees

题意:数轴上有n棵圣诞树,位置分别是xi。给m个人分配位置,使得所有人到离他最近的圣诞树的距离的和最小。

题解:直接贪心往每棵树两边加人即可。用了一个丑陋的实现,先二分确定可以加的值,然后再加。

int n, m;
int x[200005];
 
bool check(int mlen) {
    ll sumpoints = 2 * mlen;
    for(int i = 2; i <= n; ++i) {
        sumpoints += min(x[i] - x[i - 1] - 1, 2 * mlen);
        if(sumpoints >= m) {
            //printf("check mlen=%d, suc.\n", mlen);
            return true;
        }
    }
    if(sumpoints >= m) {
        //printf("check mlen=%d, suc.\n", mlen);
        return true;
    }
    //printf("check mlen=%d, fail.\n", mlen);
    return false;
}
 
int ans[200005];
 
priority_queue<pii> pq;
 
ll answer(int mlen) {
    for(int j = 1; j <= mlen; ++j)
        pq.push({-j, x[1] - j});
    for(int j = 1; j <= mlen; ++j)
        pq.push({-j, x[n] + j});
    for(int i = 1; i < n; ++i) {
        if(x[i + 1] - x[i] - 1 > 2 * mlen) {
            for(int j = 1; j <= mlen; ++j)
                pq.push({-j, x[i] + j});
            for(int j = 1; j <= mlen; ++j)
                pq.push({-j, x[i + 1] - j});
        } else {
            int dis = (x[i + 1] - x[i]);
            for(int j = 1; ; ++j) {
                if(x[i] + j == x[i + 1])
                    break;
                pq.push({-(min(j, dis - j)), x[i] + j});
            }
        }
    }
 
    ll res = 0;
    for(int j = 1; j <= m; ++j) {
        ans[j] = pq.top().second;
        res += (-pq.top().first);
        //cout << "x=" << pq.top().second << endl;
        //cout << "d=" << (-pq.top().first) << endl;
        pq.pop();
    }
    while(pq.size())
        pq.pop();
    return res;
}
 
ll bs() {
    int l = 1, r = m;
    while(1) {
        int mid = l + r >> 1;
        if(l == mid) {
            if(check(l))
                return answer(l);
            assert(check(r));
            return answer(r);
        }
        if(check(mid))
            r = mid;
        else
            l = mid + 1;
    }
}
 
 
void test_case() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &x[i]);
    if(n == 1) {
        ll sum = 0;
        int l = x[1], r = x[1];
        for(int i = 1; i <= m; ++i) {
            if(i & 1) {
                ans[i] = --l;
                sum += x[1] - l;
            } else {
                ans[i] = ++r;
                sum += r - x[1];
            }
        }
        printf("%lld\n", sum);
        sort(ans + 1, ans + 1 + m);
        for(int i = 1; i <= m; ++i)
            printf("%d%c", ans[i], " \n"[i == m]);
        return;
    }
    sort(x + 1, x + 1 + n);
    printf("%lld\n", bs());
    sort(ans + 1, ans + 1 + m);
    for(int i = 1; i <= m; ++i)
        printf("%d%c", ans[i], " \n"[i == m]);
}

但是其实可以存每棵树的l指针和r指针,当某棵树的r指针遇到另一棵树的l指针的时候就把这两个指针去掉。不过这样写起来不见得比二分好写。先二分的原因是可以避免把过多的点插进优先队列。

E - New Year Parties

题意:有n个人,每个人可以左移1、右移1或者留在原地。求最小覆盖多少个点和最大覆盖多少个点。

题解:很明显满足dp的性质。只是很容易漏掉一些转移。

int n;
int x[200005];
int dp[200005][8];
 
void test_case() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &x[i]);
        ++x[i];
    }
    sort(x + 1, x + 1 + n);
    //xi value range [2,n+1]
    //can move to value range [1,n+2]
 
    //MIN
    memset(dp, INF, sizeof(dp));
    dp[0][0] = 0;
    for(int i = 1; i <= n; ++i) {
        if(i == 1 || x[i] == x[i - 1]) {
            //move to left
            dp[i][4] = min(dp[i - 1][4], dp[i - 1][0] + 1);
            dp[i][5] = min(dp[i - 1][5], dp[i - 1][1] + 1);
            dp[i][6] = min(dp[i - 1][6], dp[i - 1][2] + 1);
            dp[i][7] = min(dp[i - 1][7], dp[i - 1][3] + 1);
            //stay here
            dp[i][2] = min(dp[i - 1][2], dp[i - 1][0] + 1);
            dp[i][3] = min(dp[i - 1][3], dp[i - 1][1] + 1);
            dp[i][6] = min(dp[i][6], min(dp[i - 1][6], dp[i - 1][4] + 1));
            dp[i][7] = min(dp[i][7], min(dp[i - 1][7], dp[i - 1][5] + 1));
            //move to right
            dp[i][1] = min(dp[i - 1][1], dp[i - 1][0] + 1);
            dp[i][3] = min(dp[i][3], min(dp[i - 1][3], dp[i - 1][2] + 1));
            dp[i][5] = min(dp[i][5], min(dp[i - 1][5], dp[i - 1][4] + 1));
            dp[i][7] = min(dp[i][7], min(dp[i - 1][7], dp[i - 1][6] + 1));
        } else {
            if(x[i] == x[i - 1] + 1) {
                //move to left
                dp[i][4] = min(min(dp[i - 1][2], dp[i - 1][6]), min(dp[i - 1][0], dp[i - 1][4]) + 1);
                dp[i][6] = min(min(dp[i - 1][3], dp[i - 1][7]), min(dp[i - 1][1], dp[i - 1][5]) + 1);
                //stay here
                dp[i][2] = min(min(dp[i - 1][1], dp[i - 1][5]), min(dp[i - 1][0], dp[i - 1][4]) + 1);
                dp[i][6] = min(dp[i][6], min(min(dp[i - 1][3], dp[i - 1][7]), min(dp[i - 1][2], dp[i - 1][6]) + 1));
                //move to right
                dp[i][1] = min(dp[i - 1][0], dp[i - 1][4]) + 1;
                dp[i][3] = min(dp[i - 1][1], dp[i - 1][5]) + 1;
                dp[i][5] = min(dp[i - 1][2], dp[i - 1][6]) + 1;
                dp[i][7] = min(dp[i - 1][3], dp[i - 1][7]) + 1;
            } else if(x[i] == x[i - 1] + 2) {
                //move to left
                dp[i][4] = min(min(dp[i - 1][1], dp[i - 1][3]), min(dp[i - 1][5], dp[i - 1][7]));
                for(int j = 0; j <= 7; j += 2)
                    dp[i][4] = min(dp[i][4], dp[i - 1][j] + 1);
                //stay here
                for(int j = 0; j <= 7; j += 2)
                    dp[i][2] = min(dp[i][2], dp[i - 1][j] + 1);
                for(int j = 1; j <= 7; j += 2)
                    dp[i][6] = min(dp[i][6], dp[i - 1][j] + 1);
                //move to right
                for(int j = 0; j <= 7; j += 2)
                    dp[i][1] = min(dp[i][1], dp[i - 1][j] + 1);
                for(int j = 1; j <= 7; j += 2)
                    dp[i][5] = min(dp[i][5], dp[i - 1][j] + 1);
            } else {
                //move to left
                for(int j = 0; j <= 7; ++j)
                    dp[i][4] = min(dp[i][4], dp[i - 1][j] + 1);
                //stay here
                for(int j = 0; j <= 7; ++j)
                    dp[i][2] = min(dp[i][2], dp[i - 1][j] + 1);
                //move to right
                for(int j = 0; j <= 7; ++j)
                    dp[i][1] = min(dp[i][1], dp[i - 1][j] + 1);
            }
        }
        /*for(int j = 0; j <= 7; ++j)
            printf("dp[%d][%d]=%d\n", i, j, dp[i][j]);
        printf("\n");*/
    }
    int ans1 = INF;
    for(int j = 0; j <= 7; ++j)
        ans1 = min(ans1, dp[n][j]);
 
 
    //MAX
    memset(dp, -INF, sizeof(dp));
    dp[0][0] = 0;
    for(int i = 1; i <= n; ++i) {
        if(i == 1 || x[i] == x[i - 1]) {
            //move to left
            dp[i][4] = max(dp[i - 1][4], dp[i - 1][0] + 1);
            dp[i][5] = max(dp[i - 1][5], dp[i - 1][1] + 1);
            dp[i][6] = max(dp[i - 1][6], dp[i - 1][2] + 1);
            dp[i][7] = max(dp[i - 1][7], dp[i - 1][3] + 1);
            //stay here
            dp[i][2] = max(dp[i - 1][2], dp[i - 1][0] + 1);
            dp[i][3] = max(dp[i - 1][3], dp[i - 1][1] + 1);
            dp[i][6] = max(dp[i][6], max(dp[i - 1][6], dp[i - 1][4] + 1));
            dp[i][7] = max(dp[i][7], max(dp[i - 1][7], dp[i - 1][5] + 1));
            //move to right
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + 1);
            dp[i][3] = max(dp[i][3], max(dp[i - 1][3], dp[i - 1][2] + 1));
            dp[i][5] = max(dp[i][5], max(dp[i - 1][5], dp[i - 1][4] + 1));
            dp[i][7] = max(dp[i][7], max(dp[i - 1][7], dp[i - 1][6] + 1));
        } else {
            if(x[i] == x[i - 1] + 1) {
                //move to left
                dp[i][4] = max(max(dp[i - 1][2], dp[i - 1][6]), max(dp[i - 1][0], dp[i - 1][4]) + 1);
                dp[i][6] = max(max(dp[i - 1][3], dp[i - 1][7]), max(dp[i - 1][1], dp[i - 1][5]) + 1);
                //stay here
                dp[i][2] = max(max(dp[i - 1][1], dp[i - 1][5]), max(dp[i - 1][0], dp[i - 1][4]) + 1);
                dp[i][6] = max(dp[i][6], max(max(dp[i - 1][3], dp[i - 1][7]), max(dp[i - 1][2], dp[i - 1][6]) + 1));
                //move to right
                dp[i][1] = max(dp[i - 1][0], dp[i - 1][4]) + 1;
                dp[i][3] = max(dp[i - 1][1], dp[i - 1][5]) + 1;
                dp[i][5] = max(dp[i - 1][2], dp[i - 1][6]) + 1;
                dp[i][7] = max(dp[i - 1][3], dp[i - 1][7]) + 1;
            } else if(x[i] == x[i - 1] + 2) {
                //move to left
                for(int j = 1; j <= 7; j += 2)
                    dp[i][4] = max(dp[i][4], dp[i - 1][j]);
                for(int j = 0; j <= 7; j += 2)
                    dp[i][4] = max(dp[i][4], dp[i - 1][j] + 1);
                //stay here
                for(int j = 0; j <= 7; j += 2)
                    dp[i][2] = max(dp[i][2], dp[i - 1][j] + 1);
                for(int j = 1; j <= 7; j += 2)
                    dp[i][6] = max(dp[i][6], dp[i - 1][j] + 1);
                //move to right
                for(int j = 0; j <= 7; j += 2)
                    dp[i][1] = max(dp[i][1], dp[i - 1][j] + 1);
                for(int j = 1; j <= 7; j += 2)
                    dp[i][5] = max(dp[i][5], dp[i - 1][j] + 1);
            } else {
                //move to left
                for(int j = 0; j <= 7; ++j)
                    dp[i][4] = max(dp[i][4], dp[i - 1][j] + 1);
                //stay here
                for(int j = 0; j <= 7; ++j)
                    dp[i][2] = max(dp[i][2], dp[i - 1][j] + 1);
                //move to right
                for(int j = 0; j <= 7; ++j)
                    dp[i][1] = max(dp[i][1], dp[i - 1][j] + 1);
            }
        }
        /*for(int j = 0; j <= 7; ++j)
            printf("dp[%d][%d]=%d\n", i, j, dp[i][j]);
        printf("\n");*/
    }
    int ans2 = 0;
    for(int j = 0; j <= 7; ++j)
        ans2 = max(ans2, dp[n][j]);
 
    printf("%d %d\n", ans1, ans2);
}

好像有贪心的写法?

最小的贪心:首先把最左侧的元素往右移,然后记录当前的lst(最右)位置,假如某个元素x<=lst+1说明他可以通过某些移动叠到lst中,跳过这种情况,否则一定断开了,递归到小规模的问题。

最大的贪心:首先把最左侧的元素往左移,然后记录当前的lst(最右位置),假如某个元素xlst,则可以把x右移;假如某个元素xlst+1那么它可以不移动,其他情况说明断开了,递归到小规模的问题。

int n;
int x[200005];

int solve_min() {
    int ans = 0;
    int lst = -INF;
    for(int i = 1; i <= n; ++i) {
        if(x[i] <= lst + 1)
            continue;
        else {
            ++ans;
            lst = x[i] + 1;
        }
    }
    return ans;
}

int solve_max() {
    int ans = 0;
    int lst = -INF;
    for(int i = 1; i <= n; ++i) {
        if(x[i] <= lst - 1)
            continue;
        else {
            ++ans;
            if(x[i] == lst)
                lst = x[i] + 1;
            else if(x[i] == lst + 1)
                lst = x[i];
            else
                lst = x[i] - 1;
        }
    }
    return ans;
}

void test_case() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &x[i]);
        ++x[i];
    }
    sort(x + 1, x + 1 + n);
    printf("%d %d\n", solve_min(), solve_max());
}
posted @ 2020-01-21 13:41  KisekiPurin2019  阅读(138)  评论(0编辑  收藏  举报