置换群

看了几天置换群,一直没搞清楚定义是怎么回事,一个置换可以写成若干循环的乘积,那么如果置换求幂的话,一个循环不会跑到另一个循环里面去。

我们可以简单理解为这几个位置的数来回换。

poj3270

给出一列数,求将这列数排成升序的最小花费,这里花费定义为交换两个数的和。
例如给出一排数8 4 5 3 2 7,那么我们最终的状态为2 3 4 5 7 8,这里的轮换有(8 2 7)(4 5 3),这里8应该在第六个位置,
而第6个位置是7,7应该在5这个位置,而第5个位置为2,2应该在1这个位置,这样就到了8所在的位置,我们称这是一个轮换。
首先需要明确的一点是对于每个群,假设有k个数,那么我们需要交换k-1次得到升序。
对于每一个群,我们有两种换发:
1.群里换,拿群里最小的数t与其他每个数交换,共k-1次,花费为:sum+(k-2)*t.
2.将这个数列最小的数m,拉入这个群,与该群最小的数t交换,然后用这个最小的数与其他数交换k-1次,然后再将m与t换回来,这样
花费为:sum+t+(k+1)m
那么最小花费我们取两者中最小的,即sum+min{(k-2)*t,t+(k+1)*m}.
对于这个题关键就是怎么确定每个群,我们利用计数排序,得到每个数应该在的位置,然后循环判断这个位置是否已经被访问了。时间
复杂度为O(n)

代码:

#include <iostream>
#include <stdio.h>
using namespace std;
const int N=100001;
const int INF=100000000;
int m[N],cnt[N+1];
bool used[N+1];
int main()
{
	int n,i,j,tmp1,tmp2,len,t,maxValue,minValue,sum=0,curSum;
	memset(cnt,0,sizeof(cnt));
	memset(used,false,sizeof(used));
	minValue=INF;
	maxValue=0;
	scanf("%d",&n);
	for(i=1;i<=n;++i)
	{
		cin>>m[i];
		cnt[m[i]]++;
		if(m[i]<minValue)
			minValue=m[i];
		if(m[i]>maxValue)
			maxValue=m[i];
	}
	//计数,得到每个数的位置,我们按照上面的那排数,得到的结果应该为6 3 4 2 1 5
	for(i=1;i<=maxValue;++i)
		cnt[i]=cnt[i]+cnt[i-1];
/*	for(i=1;i<=n;++i)
		cout<<cnt[m[i]]<<" ";
	cout<<endl;
*/
	for(i=1;i<=n;++i)
	{
		if(!used[i]) 
		{
			j=i;
			t=m[i];
			len=0;
			curSum=0;
			//首先1这个位置未访问,得到8这个数,找到8应该在的位置,是6,6这个位置未访问,得到数7,。。。依次类推
			while(!used[j])
			{
				++len;	
				if(m[j]<t)
					t=m[j];
				curSum+=m[j];
				used[j]=true;
				j=cnt[m[j]];
			}
			if(1 < len)
				sum+=curSum;
			if(2<len)
			{
				tmp1=(len-2)*t;
				tmp2=(len+1)*minValue+t;
				if(tmp1 > tmp2)
					tmp1=tmp2;
				sum=sum+tmp1;
			}
		}
	}
	cout<<sum<<endl;
	return 0;
}

 
poj2369

给出1-n的一个排列,a1,a2,...,an,表示P(1)=a1,P(2)=a2,...,P(n)=an,P(P(1))=P(a1),P(P(2))=P(a2),
...,P(P(n))=P(an).问经过多少次后使得P(1)=1,...,P(n)=n.
这个是置换群的概念题,找到每个循环节,确定其长度len1,len2,...,lenk,求他们的最小公倍数。
对于题目中给定的一个例子进行分析:
1 2 3 4 5
4 1 5 2 3
上面定义了函数P,那么我们可以看出这个置换可以写成(1 4 2)(3 5),这样循环1中的数绝对不会跑到第二个循环中,每个循环i需要经过leni次后就可以到达目的状态,所以我们只需要确定各个循环节长度的最小公倍数。

poj1026
首先给出一个置换,然后给出一个字符串,问置换k次之后得到的字符串是什么?
我们求出来子循环,然后对每个子循环计算k次之后置换群变成什么排列,用b[0],b[1],...,b[t-1]表示一个子群,那么长度为t,经过一次置换后变成b[0]=b[1],b[1]=b[2],..,b[t-1]=b[0],所以经过k次后变成
b[(0+k)%t],b[(1+k)%t],..,b[(t-1+k)%t],即b[i]->b[(i+k)%t].
代码:

#include <iostream>
#include <stdio.h>
using namespace std;
const int N=201;
int a[N],b[N],c[N];
bool visit[N];
char message[N],rs[N];
int main()
{
	int n,k,i,j,t;
	while(1)
	{
		cin>>n;
		if(!n)
			break;
		for(i=0;i<n;++i)
		{
			cin>>a[i];
			--a[i];
		}
		while(cin>>k)
		{
			if(!k)
				break;
			memset(message,'\0',sizeof(message));
			memset(rs,'\0',sizeof(rs));
			memset(visit,0,sizeof(visit));
			getchar();
			cin.getline(message,N,'\n');
			int len=strlen(message);
			if(len<N)
			{
				for(i=len;i<n;++i)
					message[i]=' ';
			}
			for(i=0;i<n;++i)
			{
				j=i;
				t=0;
				if(!visit[j])
				{
					while(!visit[j])
					{
						visit[j]=true;
						b[t]=j;
						t++;
						j=a[j];
					}
// 					for(j=0;j<t;++j)
// 						cout<<b[j]<<" ";
// 					cout<<endl;
					for(j=0;j<t;++j)
					{
					//	cout<<b[j]<<" "<<b[(j+k)%t]<<endl;
						rs[b[(j+k)%t]]=message[b[j]];
					}
				}
			}
			cout<<rs<<endl;
		}
		cout<<endl;
	}
	return 0;
}

 poj1721
洗牌机
给出洗牌规则,如果位置I上的牌是J,而且位置J上的牌是K,那么通过洗牌机后位置I上的牌将是K。
首先给出初始顺序a1,a2,...,an,在位置ai处放置ai+1,得到初始序列为x1,x2,...,xn,经过s次洗牌之后,得到新的序列p1,p2,...,pn,现在给出最终序列即s,让我们求xi。这个完全可以用模拟来做,但是时间复杂度可能会高,我们可以求出循环节t,那么s=s%t之后,我们在进行洗n-s次就可以了。

#include <iostream>
using namespace std;
const int N=1001;
int a[N],b[N],m[N];
int main()
{
	int i,n,s,j,t;
	cin>>n>>s;
	for(i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		m[i]=a[i];
	}
//	memset(used,0,sizeof(used));
	//找到循环节
	t=0;
	bool flag;
	while(1)
	{
		for(i=1;i<=n;++i)
			b[i]=a[a[i]];
		++t;
		flag=true;
		for(i=1;i<=n;++i)
		{
			if(b[i]!=m[i])
			{
				flag=false;
				break;
			}
		}
		if(flag)
			break;
		for(i=1;i<=n;++i)
			a[i]=b[i];
	}
	s=s%t;
	t=t-s;
	for(i=1;i<=n;++i)
			a[i]=b[i];
	while(t--)
	{
		for(i=1;i<=n;++i)
			b[i]=a[a[i]];
		for(i=1;i<=n;++i)
			a[i]=b[i];
	}
	for(j=1;j<=n;++j)
		printf("%d\n",b[j]);
	return 0;
}

 

 

 

posted on 2011-11-15 12:07  buptLizer  阅读(7487)  评论(0编辑  收藏  举报

导航