#include <vector>
#include <iostream>
// 【】: 計算量
// L : レーンの長さ
// P : 多重度
// S : すしの数
// T : すしの時間合計
// T ≦ PL
//【P(L+S+PP)】
struct SUSHI {
int pos;
int time;
};
struct LANE {
std::vector < SUSHI > sushi;
int length;
};
struct LANE_INFO {
std::vector < int > pile;
int pile_max;
std::vector < std::vector < int > > pos2sushi;
};
// nをLで割った余り [0~L-1]
int modulo(int n, int L){
return n < 0 ? L - 1 - (- n - 1) % L : n % L;
}
// a |= b
void or_equal(std::vector < bool > &a, const std::vector < bool > &b){
if (a.size() < b.size()){
a.resize(b.size(), false);
}
for (int i = 0; i < b.size(); i++){
a[i] = a[i] || b[i];
}
}
// (a & b) != 0
bool intersect(const std::vector < bool > &a, const std::vector < bool > &b){
for (int i = 0; i < a.size() && i < b.size(); i++){
if (a[i] && b[i]){
return true;
}
}
return false;
}
// 情報取得
//【T】
void get_lane_info(const LANE &lane, LANE_INFO &info){
int L = lane.length;
// 多重度
//【T】
info.pile.clear();
info.pile.resize(L, 0);
for (int i = 0; i < lane.sushi.size(); i++){
const SUSHI &sushi = lane.sushi[i];
for (int j = 0; j < sushi.time; j++){
info.pile[modulo(sushi.pos + j, L)] ++;
}
}
// 多重度最大値
//【L】
info.pile_max = 0;
for (int i = 0; i < L; i++){
if (info.pile[i] >= info.pile_max){
info.pile_max = info.pile[i];
}
}
// レーン位置からSUSHIデータ情報へ
//【S】
info.pos2sushi.clear();
info.pos2sushi.resize(L);
for (int i = 0; i < lane.sushi.size(); i++){
const SUSHI &sushi = lane.sushi[i];
info.pos2sushi[sushi.pos].push_back(i);
}
}
// オーバーラン量計算
//【LP+SP+PPP】
int get_overrun(const LANE &lane, int pos){
int L = lane.length;
LANE_INFO info;
get_lane_info(lane, info); //【T】
//【L】
std::vector < int > over;
for (int i = 0; i < lane.sushi.size(); i++){
const SUSHI &sushi = lane.sushi[i];
int n = modulo(pos - sushi.pos, L);
if (0 < n && n < sushi.time){
over.push_back(i);
}
}
//【P】
std::vector < std::vector < bool > > connect(L, std::vector < bool >(over.size(), false));
for (int i = 0 ; i < over.size() ; i++){
const SUSHI &sushi = lane.sushi[over[i]];
connect[sushi.pos][i] = true;
connect[modulo(sushi.pos + sushi.time, L)][i] = true;
}
//【LP+SP】
for (int i = 0; i < L; i++){
int p = modulo(pos + i, L);
if (info.pile[p] < info.pile_max){
or_equal(connect[modulo(p + 1, L)], connect[p]);
}
for (int j = 0; j < info.pos2sushi[p].size(); j++){
const SUSHI &sushi = lane.sushi[info.pos2sushi[p][j]];
or_equal(connect[modulo(p + sushi.time, L)], connect[p]);
}
}
//【PP】
std::vector < std::vector < bool > > connect0;
connect0.push_back(connect[pos]);
for (int i = 0; i < over.size(); i++){
connect0.push_back(connect[lane.sushi[over[i]].pos]);
}
for (int i = 0; i < connect0.size(); i++){
for (int j = i + 1; j < connect0.size(); j++){
if (intersect(connect0[i], connect0[j])){
or_equal(connect0[i], connect0[j]);
if (j < connect0.size() - 1){
connect0[j].swap(connect0.back());
}
connect0.pop_back();
j = i;
}
}
}
//【PP】
int over_max = 0;
for (int i = 1; i < connect0.size(); i++){
int over_min = L;
for (int j = 0; j < over.size(); j++){
if (connect0[i][j]){
const SUSHI &sushi = lane.sushi[over[j]];
int o = modulo(sushi.pos + sushi.time - pos, L);
if (o < over_min){
over_min = o;
}
}
}
if (over_min > over_max){
over_max = over_min;
}
}
return over_max;
}
// 最短時間計算
//【LP+SP+PPP】
int get_min_time(LANE lane, int start = 0, int time = 0){
int L = lane.length;
if (L == 0){
return 0;
}
// パラメーターの調整 ( 位置:[0~L-1], 長さ:[1~L]に )
//【S】
for (int i = 0; i < lane.sushi.size(); i++){
SUSHI &sushi = lane.sushi[i];
int l = modulo(sushi.time-1, L) + 1;
time -= l - sushi.time;
sushi.time = l;
sushi.pos = modulo(sushi.pos, L);
}
LANE_INFO info;
get_lane_info(lane, info); //【T】
if (info.pile_max == 0){
return 0;
}
// 多重度最大の先頭を調べる
//【L】
int pile_max_top = 0;
for (int i = 0; i < L; i++){
if (info.pile[modulo(start + i, L)] >= info.pile_max){
pile_max_top = modulo(start + i + 1, L);
}
}
// 分解1
int ov1 = get_overrun(lane, start); //【LP+SP+PPP】
if (ov1 != 0 || pile_max_top == start){
return info.pile_max * L + ov1 + time;
}
// 分解2
SUSHI dummy = { pile_max_top, modulo(start - pile_max_top, L) };
lane.sushi.push_back(dummy);
int ov2 = get_overrun(lane, pile_max_top); //【LP+SP+PPP】
return (info.pile_max - 1) * L + modulo(pile_max_top - start, L) + ov2 + time;
}
const char * const LANE_DATA[] = {
"12_3",
"313__",
"4_35_1264_23_434",
"123456789123456789",
"88967472612377988186",
"19898693316679441672",
"93769682716711132249893",
"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999",
"123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789",
NULL
};
int main(){
for (int i = 0 ; LANE_DATA[i] != NULL ; i++){
LANE lane;
int L = 0;
for (int j = 0; ; j++){
char ch = LANE_DATA[i][j];
if (ch == '\0') {
break;
}
if ('1' <= ch && ch <= '9'){
SUSHI sushi = { j, ch - '0' };
lane.sushi.push_back(sushi);
}
L++;
}
lane.length = L;
std::cout << "----------------------------------------------------------------\n";
std::cout << LANE_DATA[i] << "\n";
std::cout << "time : " << get_min_time(lane) << "\n";
}
return 0;
}