hdu4259 Double Dealing (置换群).

热身赛的1003,,水题啊,可是我折腾了一天。
首先要先发一次牌,得到一个顺序,再根据这个顺序获得各个循环节的长度,对循环节求最小公倍数,便是总的最少移动次数.
题意是,n张牌每次发给k个人,再堆叠起来重新发:比如样例 10 3
原始状态是:   1 2 3 4 5 6 7 8 9 10
一次过后:      10 7  4 1 8 5 2 9 6 3
第二次 3 2 1 10 9 8 7 6 5 4  
第三次 4 7 10 3 6 9 2 5 8 1 
第四次 1 2 3 4 5 6 7 8  9 10 
根据做上一题的经验我们知道只要第一次的牌,我们可以求得循环节,再根据循环节长度就可以做出了.
怎么模拟出第一次洗牌后的序列呢?

1 2 3 
4 5 6
7 8 9
10 11 12
然后每次取一列.\
T_T,我卡在了最后求最小公倍数上了,我是先乘了再除了的,问了群神就醒悟过来"这样会溢出"改成先除再乘,马上就过了^_^
1 2 3 4 5 6 7 8 9 10用二维数组这样存起来:



/*
Problem ID:hdu 4259
Meaning:
Analyzing:置换群+lcm(循环节长度)
*/
#include <iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>

using namespace std;
typedef struct even{int y1,y2,x;}even;

#define FOR(i,s,t) for(int i=(s); i<(t); i++)
#define LL long long
#define BUG puts("here!!!")
#define STOP system("pause")
#define file_r(x) freopen(x, "r", stdin)
#define file_w(x) freopen(x, "w", stdout)

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 808
int A[maxn][maxn],V[maxn];
LL gcd(LL a,LL b) {return a?gcd(b%a,a):b;}
int main(){
    int n,k;
    while(~scanf("%d%d",&n,&k)&&(n||k)){
        int cnt=1,i;
        for(i=0;;i++){
            for(int j=0;j<k;j++)
                A[i][j]=cnt++;
            if(cnt>n) break;
        }
        int M=1;
        for(int j=0;j<k;j++){
            for(int m=i;m>=0;m--){
                if(A[m][j]<=n&&A[m][j])
                    V[M++]=A[m][j];
            }
        }
     int vis[maxn];
     LL res=1,len;
     memset(vis,0,sizeof(vis));
     for(i=1;i<=n;i++){
        if(!vis[i]){
            len=0;
            int t=i;
            while(1){
                if(vis[t]) break;
                len++;
                vis[t]=1;
                t=V[t];
            }
            res=res/gcd(len,res)*len;
        }
     }
     printf("%I64d\n",res);
    }
    return 0;
}


再贴下标程,很赞0.0

#include <iostream>
#include <vector>
#include <cstring>

using namespace std;

long long gcd(long long a, long long b) { return a ? gcd(b % a, a) : b; }

int A[1010];
bool vis[1010];

int main() {
  int N, K;
  for(cin >> N >> K; N || K; cin >> N >> K) {
    /* Compute the permutation induced by the shuffle. */
    int M = 0;
    for(int i = 0; i < K && i < N; i++) {
      for(int j = (N - i - 1) / K * K + i; j >= 0; j -= K) {
        A[M++] = j;
		cout<<M-1<<" --> "<<A[M-1]<<endl;
      }
    }

    /* Compute the lcm of the cycle lengths of the permutation. */
    long long res = 1;
    memset(vis, 0, sizeof(vis));
    for(int i = 0; i < N; i++) {
      int x = i;
      int ln = 0;
      while(!vis[x]) {
        vis[x] = true;
        x = A[x];
        ln++;
      }

      /* Careful to divide then multiply here. */
      if(ln) res = res / gcd(res, ln) * ln;
    }
    cout << res << endl;
  }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值