组合数学--置换群

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














评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值