https://codeforces.com/problemset/problem/1215/E
设f[i][j]为i在houmian,j在后的 颜色 i,j之间需要交换的次数,那么从前枚举到后,当前的颜色a[i],如果a[i]要到某个颜色j前面,那么f[a[i]][j]+=num[j],即当前这个a[i]要和之前已经出现过的num[j]个j颜色的全部交换一次。
最后只要枚举任意两种颜色的相对关系,选择f[i][j]和f[j][i]中较小的作为交换次数就行了。
upd: 上述贪心是错的,这个方法被hack了,不过好像没有unrated。。。
因为如果选择了 (1,2) (2,3) (3,1) ,即f[1][2]<f[2][1],f[2][3]<f[3][2],f[3][1]<f[1][3],那么这个先后顺序就矛盾了。需要用状压DP
我们设dp[s]表示把s这个状态中所有数字安排了最少需要多少次交换,然后枚举哪一个放到最前面就行了。
#include<bits/stdc++.h>
#define maxl 400010
using namespace std;
const long long inf=1ll<<61;
int n,m;
int a[maxl],num[21];
long long ans;
long long f[21][21],dp[1<<21];
char s[maxl];
inline void prework()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
num[a[i]]++;
for(int j=1;j<=20;j++)
if(j!=a[i])
f[a[i]][j]+=num[j];
}
}
inline void mainwork()
{
for(int i=0;i<(1<<21);i++)
dp[i]=inf;
dp[0]=0;int t;long long tmp=0;
for(int s=1;s<(1<<20);s++)
{
for(int i=1;i<=20;i++)
if(s&(1<<(i-1)))
{
t=s^(1<<(i-1));tmp=0;
for(int j=1;j<=20;j++)
if(t&(1<<(j-1)))
tmp+=f[i][j];
dp[s]=min(dp[s],dp[t]+tmp);
}
}
ans=dp[(1<<20)-1];
}
inline void print()
{
printf("%lld",ans);
}
int main()
{
int t=1;
//scanf("%d",&t);
for(int i=1;i<=t;i++)
{
prework();
mainwork();
print();
}
return 0;
}
下面这个是之前的错程序。
#include<bits/stdc++.h>
#define maxl 400010
using namespace std;
int n,m;
int a[maxl],num[21];
long long ans;
long long f[21][21];
char s[maxl];
inline void prework()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
num[a[i]]++;
for(int j=1;j<=20;j++)
if(j!=a[i])
f[a[i]][j]+=num[j];
}
}
inline void mainwork()
{
for(int i=1;i<=20;i++)
for(int j=1;j<=20;j++)
if(i!=j)
ans+=min(f[i][j],f[j][i]);
}
inline void print()
{
ans=ans/2;
printf("%lld",ans);
}
int main()
{
int t=1;
//scanf("%d",&t);
for(int i=1;i<=t;i++)
{
prework();
mainwork();
print();
}
return 0;
}