codeforces1215E Marbles

 

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]

我们设dp[s]表示把s这个状态中所有数字安排了最少需要多少次交换,然后枚举哪一个放到最前面就行了。

#include
#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
#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;
}

 

 

你可能感兴趣的:(DP,状态压缩)