【差分+DP】Gym101620K Kitchen Knobs

S o u c e : Souce: Souce:2017-2018 ACM-ICPC, Central Europe Regional Contest (CERC 17)

P r o b l e m : Problem: Problem:
n = 500 n=500 n=500个长度为7的整数。需要把每个整数都变成最大循环表示,每次操作可以使连续的一段整数循环移动k位(往左/往右),求最少操作。

I d e a : Idea: Idea:
除去每一位都相同的整数,剩下的数的最大循环表示必定是唯一的,那么题目转化为一段序列 a [ 1 ] . . a [ n ] a[1].. a[n] a[1]..a[n],最少的区间加/区间减,使得每个数模7为0。于是考虑差分,把区间操作改成点操作,发现可以贪心配对 1 − 6 , 2 − 5 , 3 − 4 1-6,2-5,3-4 162534,于是对剩下三种数进行dp,将剩下的数分成尽量多的集合,使得每个集合的和模7为0。

C o d e : Code: Code:

#include<bits/stdc++.h>
using namespace std;

#define I inline

const int N = 510;

short a[N], c[10], b[N][4], dp[N][N][N];

I void up(short &a, const short &b) { if(a > b) a = b; };

I void work() {
    int n, n1 = 1; scanf("%d", &n);
    char s[10];
    for(int i = 0; i < n; i++) {
        scanf("%s", s);
        int now = 0; bool flag = 0;
        for(int j = 0; j < 7; j++) {
            if(j && s[j]!=s[j-1]) flag = 1;
            int tmp = 0;
            for(int k = 0; k < 7; k++) tmp = tmp*10+s[(j+k)%7]-'0';
            if(tmp > now) { now = tmp; a[n1] = j; }
        }
        if(!flag) continue;
        n1++;
    }
    n = n1;
    for(int i = n; i >= 1; i--) {
        a[i] = (a[i]-a[i-1]+7)%7;
        c[a[i]]++;
    }
    int ans = 0, x = 1, y = 2, z = 3;
    int t = min(c[1], c[6]); ans += t;
    if(c[1] -= t) x = 1; if(c[6] -= t) x = 6;
    t = min(c[2], c[5]); ans += t;
    if(c[2] -= t) y = 2; if(c[5] -= t) y = 5;
    t = min(c[3], c[4]); ans += t;
    if(c[3] -= t) z = 3; if(c[4] -= t) z = 4;

    int m = 0;
    for(int i = 0; i <= 7; i++) for(int j = 0; j <= 7; j++)
    for(int k = 0; k <= 7; k++) if(i+j+k && (i*x+j*y+k*z)%7==0){
        b[m][0] = i, b[m][1] = j, b[m][2] = k;
        b[m++][3] = i+j+k-1;
    }
    x = c[x], y = c[y], z = c[z];
    for(int i = x; i >= 0; i--) for(int j = y; j >= 0; j--)
    for(int k = z; k >= 0; k--) if(x-i+y-j+z-k) {
        short t = 0x3f3f;
        for(int l = 0; l < m; l++) if(i+b[l][0]<=x && j+b[l][1]<=y && k+b[l][2]<=z){
            up(t, dp[i+b[l][0]][j+b[l][1]][k+b[l][2]]+b[l][3]);
        }
        dp[i][j][k] = t;
    }
    printf("%d\n", ans+dp[0][0][0]);
}

int main() {
    work();
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值