题目1
题意:
有n个数,每个数都有一种颜色。每次操作可以交换两个元素的位置,要求使得相同颜色的元素连续排列的最小次数。
分析:
看到颜色数只有20,就可以往网络流或者状压方向去想。分析过后可以想出状压的解法。压缩颜色为状态,0代表这个颜色尚未处理,1代表这个颜色已经处理好了。处理好的颜色统一放到左边。
考虑转移,对于一个状态,它可以由当前状态的某一个1变为0的那个状态转移过来。代价是多少呢?就是当前那个1对应颜色前的那些未被处理的颜色个数,即对应的状态为0的那些颜色。对于计算这些颜色,我们可以预处理一下,num[i][j]表示在i颜色前的颜色j的数量。用这些来算代价,转移即可。
#include <iostream>
using namespace std;
typedef long long ll;
int a[400005];
ll cnt[30],num[30][30];
ll dp[1<<20];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
for (int j = 0; j < 20; j++)
{
num[a[i]-1][j] += cnt[j];
}
cnt[a[i]-1] ++;
}
for (int i = 1; i < (1<<20); i++)
{
dp[i] = 1e18;
for (int j = 0; j < 20; j++)
{
if( ((i >> j) & 1) == 0 ) continue;
ll temp = 0;
for (int k = 0; k < 20; k++)
{
if( (i>>k) & 1 ) continue;
temp += num[j][k];
}
dp[i] = min(dp[i],dp[i^(1<<j)]+temp);
}
}
cout << dp[(1<<20)-1] << '\n';
return 0;
}