poj1721
题意:给出一个由n个数组成的经过m次置换(即把a[i]=a[a[i]])后的群,求原来的那个群。
找出循环节 ----- 直接模拟!!
如 案列:n=7,m=4; 6 3 1 2 4 7 5
一次变化 7 1 6 3 2 5 4
二次变化 4 7 5 6 1 2 3
三次变化 6 3 1 2 4 7 5 ←到这里 即找到 循环节为 cnt=3;
那么原群就等于 m%=cnt; m=cnt-m; 从案列的数列经过m次循环就可以到 原群了!
#include<iostream>
#include<cstdio>
using namespace std;
#define N 1010
int a[N],b[N],c[N];
int n,m;
int solve()
{
int ans=0;
while(1)
{
int i,j;
ans++;
for( i=1;i<=n;i++)
b[i]=a[a[i]];
for( j=1;j<=n;j++)
if(c[j]!=b[j])
break;
if(j==n+1) break;
for(int k=1;k<=n;k++)
a[k]=b[k];
}
return ans;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=c[i]=a[i];
}
int cnt=solve();
m%=cnt;
m=cnt-m;
while(m--)
{
for(int i=1;i<=n;i++)
a[i]=c[c[i]];
for(int j=1;j<=n;j++)
c[j]=a[j];
}
for(int i=1;i<=n;i++)
printf("%d\n",c[i]);
}
return 0;
}
poj3270
题意:给出一列数,求将这列数排成升序的最小花费,这里花费定义为交换两个数的和。
第一步:要求出每一个小轮换的循环节。
第二步:求一个小轮换最小值。
①:第一种方法:用这个轮换中的最小值,与每一个值交换。求出交换的和 sum1.
②:第二种方法:用整个数列中的最小值 与 这个轮换中的最小值交换,再执行①,最后再把最小值换回来。求的和.sum2.
③:求min(sum1,sum2)看哪个小!就把它加入答案中.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 10100
int MIN;
int n;
struct Node
{
int temper;
int id;
bool operator<(const Node a) const
{
return temper<a.temper;
}
}cow[N];
bool vis[N];
int solve()
{
int cnt;
int ans=0;
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
vis[i]=true;
int sum=cow[i].temper;
int m=cow[i].temper;
int j=cow[i].id;
cnt=0;
while(i!=j)
{
cnt++;
vis[j]=true;
sum+=cow[j].temper;
if(m>cow[j].temper) {m=cow[j].temper;}
j=cow[j].id;
}
ans+=min(sum-m+m*cnt,sum+m+MIN*(cnt+2));
}
}
return ans;
}
int main()
{
while(~scanf("%d",&n))
{
MIN=100000000;
for(int i=1;i<=n;i++)
{
scanf("%d",&cow[i].temper);
cow[i].id=i;
if(MIN>cow[i].temper) {MIN=cow[i].temper;}
}
sort(cow+1,cow+n+1);
printf("%d\n",solve());
}
return 0;
}
poj2369
题解:多少步到单位群。
求出每个 轮换的 循环节 求最小公倍数就行了。
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define N 2005
int d[N];
int t[N],n;
void get_time(int i)
{
int count=1;
int j=d[i];
while(i!=j)
{
count++;
j=d[j];
}
t[i]=count;
}
int gcd(int a,int b)
{
if(!b) return a;
else return gcd(b,a%b);
}
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
int _LCM()
{
int ans=t[1];
for(int i=2;i<=n;i++)
{
ans=lcm(t[i],ans);
}
return ans;
}
int main()
{
int i;
while(~scanf("%d",&n)&&n)
{
memset(d,0,sizeof(d));
memset(t,0,sizeof(t));
for(i=1;i<=n;i++)
{
scanf("%d",&d[i]);
}
for(i=1;i<=n;i++)
get_time(i);
printf("%d\n",_LCM());
}
return 0;
}
poj1026
首先给出一个置换,然后给出一个字符串,问置换k次之后得到的字符串是什么?
同样的求出每个轮换的 循环节,然后k%循环节,求出这个轮换中每个元素的位置保存下来就行了。
#include <iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 212
int n;
int key[N];
int t[N];
inline void get_time()
{
int count=0;
for(int i=1;i<=n;i++)
{
count=1;
int j=key[i];
while(i!=j)
{
count++;
j=key[j];
}
t[i]=count;
}
}
char res[N];
char str[N];
int main()
{
while(~scanf("%d",&n)&&n)
{
memset(t,0,sizeof(int)*(n+2));
for(int i=1;i<=n;i++)
{
scanf("%d",&key[i]);
}
get_time();
int k;
while(~scanf("%d",&k)&&k)
{
getchar();
gets(str+1);
for (int i=strlen(str+1)+1;i<=n;i++) str[i]=' ';
str[n+1]='\0';
for(int i=1;i<=n;i++)
{
int pos=i;
for(int j=0;j<k%t[i];j++)
{
pos=key[pos];
}
res[pos]=str[i];
}
res[n+1]='\0';
printf("%s\n",res+1);
}
puts("");
}
return 0;
}