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
1−6,2−5,3−4,于是对剩下三种数进行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;
}