zoukankan      html  css  js  c++  java
  • 置换群

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

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

    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;
    }
    
  • 相关阅读:
    艾伟:WCF中通过Dispose有效实现重用 狼人:
    艾伟:用 IIS 7、ARR 與 Velocity 建置高性能的大型网站 狼人:
    艾伟:表达式树和泛型委托 狼人:
    艾伟:jQuery性能优化指南(2) 狼人:
    艾伟:在Windows Mobile上实现自动拼写和匹配建议 狼人:
    艾伟:Web.config配置文件详解 狼人:
    艾伟:对 String 的几个错误认识 狼人:
    艾伟:ASP.NET安全问题--Forms验证的具体介绍(上篇) 狼人:
    艾伟:基于web信息管理系统的权限设计分析和总结 狼人:
    艾伟:[你必须知道的.NET]第三十一回,深入.NET 4.0之,从“新”展望 狼人:
  • 原文地址:https://www.cnblogs.com/buptLizer/p/2249551.html
Copyright © 2011-2022 走看看