P3694 邦邦的大合唱站队 解题报告
题意
\(N\) 个偶像排成一列,他们来自 \(M\) 个不同的乐队。每个团队至少有一个偶像。
现在要求重新安排队列,使来自同一乐队的偶像连续的站在一起。重新安排的办法是,让若干偶像出列(剩下的偶像不动),然后让出列的偶像一个个归队到原来的空位,归队的位置任意。
请问最少让多少偶像出列?
对于全部数据,\(1\le N\le 10^5, M\le 20\)。
思路
设计状态\(f_i\)代表状态为\(i\)的已完成队列,需要出队的最少人数。
举个例子,若\(f_i=011\),代表团队一和二站满了前排。
然后我们枚举每一个情况\(i\),对应的枚举所有的在\(i\)中的\(j\)(上面例子的话,\(j=1,2\)):
\[f[i]=min\{f[i\ xor \ 2^j]+num[j] -lenth[x,y][j]\}
\]
\(num[j]\)代表团队\(j\)的总人数,\(lenth[x,y]\)代表在区间\([x,y]\)中团队\(j\)的总人数。
显然\([x,y]\)应该是接在状态\(i\ xor\ 2^j\)后\(num[j]\)的那一段区间。
转移一下就好了,复杂度\(O(m2^m)\)。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e6 + 5;
int n, m, x, dp[N], sum[100010][30], cnt[30], qwq[N];
void dfs2(int x, int sum);
bool check(int sum, int x)
{
return (sum & (1 << (x - 1)));
}
void dfs1(int x, int sum)
{
if (x == m)
{
return ;
}
qwq[sum | (1 << x)] = qwq[sum] + cnt[x + 1];
dfs1(x + 1, sum | (1 << x));
dfs2(x + 1, sum | (1 << x));
}
void dfs2(int x, int sum)
{
if (x == m)
{
return ;
}
dfs1(x + 1, sum);
dfs2(x + 1, sum);
}
signed main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++)
{
cin >> x;
for (int j = 1; j <= m; j ++)
{
sum[i][j] = sum[i - 1][j];
}
sum[i][x] ++;
cnt[x] ++;
}
dfs2(0, 0);
dfs1(0, 0);
memset(dp, 0x3f, sizeof(dp));
dp[0] = 0;
for (int i = 1; i < (1 << m); i ++)
{
for (int j = 1; j <= m; j ++)
{
if (check(i,j))
{
int l = qwq[i ^ (1 << j - 1)];
dp[i] = min(dp[i], dp[i ^ (1 << (j - 1))] + (qwq[i] - l) - (sum[qwq[i]][j] - sum[l][j]));
}
}
}
cout << dp[(1 << m) - 1] << endl;
}

浙公网安备 33010602011771号