题目
思路
似乎想到一个状态,感觉不甚靠谱。
正解:
似乎和我的思考有一些相似之处,然而我是把出现次数放进 d p dp dp 数组的状态里,这样是不好搞的,这就是我放弃自己思路的原因。
而题解里选择的方法是:直接二分这个最少的出现次数——似乎很可行?
定义 d p [ i ] [ j ] dp[i][j] dp[i][j] :考虑前 i i i 位,状态(即为数组出现的不可重集)为 j j j 可得到的最长序列长度。满足二分出来的最小长度 x x x。转移似乎很简单了,考虑 x x x 个和 x + 1 x + 1 x+1 个就好了。
Code
LL n, a[MAXN], dp[MAXN][MAXM], Cnt, Cur[MAXK];
// dp[i][j] : 到第 i 位,出现集合为 j 的最大长度
vector<LL> Pos[MAXK];
inline LL Check(LL x)
{
memset(Cur, 0, sizeof Cur);
memset(dp, -1, sizeof dp);
dp[1][0] = 0;
for (Int i = 1; i <= n; ++ i)
{
for (Int j = 0; j < (1 << 8); ++ j)
{
if (dp[i][j] == -1)
continue;
for (Int k = 1; k <= 8; ++ k)
{
if (j & (1 << (k - 1)))
continue;
LL To = Cur[k] + x - 1; // Cur : 第几个 k
if (To >= (int) Pos[k].size())
continue;
dp[Pos[k][To]][j | (1 << (k - 1))] = Max(dp[Pos[k][To]][j | (1 << (k - 1))], dp[i][j]);
if (To + 1 >= (int) Pos[k].size())
continue;
To ++;
dp[Pos[k][To]][j | (1 << (k - 1))] = Max(dp[Pos[k][To]][j | (1 << (k - 1))], dp[i][j] + 1);
}
}
Cur[a[i]] ++;
}
LL Res = -1;
for (Int i = 1; i <= n; ++ i)
Res = Max(Res, dp[i][(1 << 8) - 1]);
if (Res == -1)
return -1;
return Res * (x + 1) + (8 - Res) * x;
}
LL Ans;
int main()
{
read( n );
for (Int i = 1; i <= n; ++ i)
{
read( a[i] );
Pos[a[i]].push_back( i );
}
LL l = 1, r = 1000;
while (l + 1 < r)
{
// printf("%lld %lld\n", l, r);
LL Mid = (l + r) / 2;
LL Get = Check( Mid );
if (Get != -1)
Ans = Get, l = Mid;
else r = Mid - 1;
}
Ans = Max(Check( l ), Check( r ));
if (Ans == -1)
{
Ans = 0;
for (Int i = 1; i <= 8; ++ i)
if ( Pos[i].size() )
Ans ++;
}
printf("%lld", Ans);
return 0;
}