置换群

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

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

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;

}

 

 

 

你可能感兴趣的:(置换群)