• 2017.11.30 算法分析与设计----运动员循环赛算法


    问题描述:有n个运动员进行循环赛,要求设计满足一下要求的日程表

    1、 每两人必须比赛一次且只比赛一次
    2、 每个选手每天只能比赛一次
    3、 要求比赛时间尽可能短(即n为偶数时比赛n-1天,n为奇数时比赛n天)

    一、分治法
    算法思想,先算n/2的日程表,然后将循环赛日程表左上复制到右下,左下复制到右上,得到n的日程表,递归实现
    实现代码:

    //循环赛日程表
    #include <stdio.h>
    #define N 1000
    int a[N][N];
    int b[N];
    inline bool odd(int n)
    {
        return n & 1;
    }
    void copy(int n)//将左上角抄到右下角,将右上角加n/2后抄到左下角,将左下角抄到右上角
    {
        int m=n/2;
        int i,j;
        for(i=1;i<=m;++i)
            for(j=1;j<=m;++j)
            {
                a[i][j+m]=a[i][j]+m;//将左上角抄到右下角
                a[i+m][j]=a[i][j+m];//将右上角加n/2后抄到左下角
                a[i+m][j+m]=a[i][j];//将左下角抄到右上角
            }
    }
    void copyodd(int n)//n/2为奇数时的复制,让轮空选手与下一个为参赛选手进行比赛
    {
        int m=n/2;
        int i,j;
        for(i=1;i<=m;++i)
        {
            b[i]=m+i;
            b[m+i]=b[i];
        }
        for(i=1;i<=m;++i)
        {
            for(j=1;j<=m+1;++j)
            {
                if(a[i][j]>m)
                {
                    a[i][j]=b[i];
                    a[m+i][j]=(b[i]+m)%n;
                }
                else
                    a[m+i][j]=a[i][j]+m;
            }
            for(j=2;j<=m;++j)
            {
                a[i][m+j]=b[i+j-1];
                a[b[i+j-1]][m+j]=i;
            }
        }
    }
    void makecopy(int n)
    {
        if(n/2>1 && odd(n/2)) copyodd(n);
        else copy(n);
    }
    void tour(int n)
    {
        if(n==1)
        {
            a[1][1]=1;
            return;
        }
        if(odd(n))
        {
            tour(n+1);//当n为奇数,就设置一个虚拟的n+1,然后就有偶数个人了。。。。和一休的那个分马很像啊
            return;
        }
        tour(n/2);
        makecopy(n);
    }
    void out(int n)
    {
    if(n==1)
        {
            printf("1
    ");
            return;
        }
        int i,j;
        int m;
        if(odd(n))
            m=n+1;
        else
            m=n;
        for(i=1;i<=n;++i)
        {
            for(j=1;j<=m;++j)
            {
                if(a[i][j]>n)//当比赛日程是与那位虚拟出来的n+1号选手比赛时,输出0,代表轮空
                    printf("0 ");
                else
                    printf("%d ",a[i][j]);
            }
            printf("
    ");
        }
    }
    int main()
    {
        int n;
        while(scanf("%d",&n),n)
        {
            tour(n);
            out(n);
        }
        return 0;
    }
    

    2、多边形法(我的实现),通过旋转多边形的一种巧妙方法

    //循环赛日程表
    #include <stdio.h>
    #define N 1000
    int a[N][N];
    inline bool odd(int n)
    {
        return n & 1;
    }
    void init()
    {
        int i;
        for(i=0;i<N;++i)
            a[i][0]=i;
    }
    void tour(int n)
    {
        if(odd(n))
            tour(n+1);
        else
        {
            int i,j,k;
            int m=n-1;
            int p,q;
            for(i=1;i<=m;++i)//第一天到第n-1天
            {
                j=i;
                p=j+1;
                if(p>m) p=p-m;
                a[p][i]=n;
                a[n][i]=p;
                for(k=0;k<=n/2-2;++k)
                {
                    q=j-k;
                    p=j+k+2;
                    if(p>m) p-=m;
                    if(q<=0) q+=m;
                    a[q][i]=p;
                    a[p][i]=q;
                }
            }
        }
    }
    void out(int n)
    {
           if(n==1)
        {
            printf("1
    ");
            return;
        }
        int i,j;
        int m;
        if(odd(n))
            m=n+1;
        else
            m=n;
        for(i=1;i<=n;++i)
        {
            for(j=0;j<m;++j)
            {
                if(a[i][j]>n)
                    printf("0 ");
                else
                    printf("%d ",a[i][j]);
            }
            printf("
    ");
        }
    }
    int main()
    {
        int n;
        init();
        while(scanf("%d",&n),n)
        {
            tour(n);
            out(n);
        }
        return 0;
    }
    

    4、 多边形法(王晓东算法设计与分析教材上的实现,比较费解,比较牛逼)

    #include <stdio.h>
    #define N 1000
    int a[N][N];
    int b[N];
    inline bool odd(int n)
    {
        return n & 1;
    }
    void init()
    {
        int i;
        for(i=0;i<N;++i)
            a[i][0]=i;
    }
    void tour(int n)
    {
        a[n][1]=n;
        if(n==1) return;
        int m=odd(n) ? n : n-1;
        int i,j,k,r;
        for(i=1;i<=m;++i)
        {
            a[i][1]=i;
            b[i]=i+1;
            b[m+i]=i+1;
        }
        for(i=1;i<=m;++i)
        {
            a[1][i+1]=b[i];
            a[b[i]][i+1]=1;
            for(j=1;j<=m/2;++j)
            {
                k=b[i+j];
                r=b[i+m-j];
                a[k][i+1]=r;
                a[r][i+1]=k;
            }
        }
    }
    void out(int n)
    {
        if(n==1)
        {
            printf("1
    ");
            return;
        }
        int i,j;
        int m;
        if(odd(n))
            m=n+1;
        else
            m=n;
        for(i=1;i<=n;++i)
        {
            for(j=1;j<=m;++j)
            {
                if(a[i][j]>n)
                    printf("0 ");
                else
                    printf("%d ",a[i][j]);
            }
            printf("
    ");
        }
    }
    int main()
    {
        int n;
        init();
        while(scanf("%d",&n),n)
        {
            tour(n);
            out(n);
        }
        return 0;
    }
    
  • 相关阅读:
    2015年5月1日 转载--各种变量在内存中的分布
    2015年4月30日 计算两个日期天数,写的代码
    2015年4月29日 dayofweek
    2015年4月28日
    2015年4月28日----高大上的数组,进制准换,最多是35进制
    2015年4月27日---C语言:输出特殊图案,请在c环境中运行,看一看,Very Beautiful!
    2015年4月27日
    《C++ Primer》学习笔记:迭代器介绍
    《C++ Primer》学习笔记:3.3.3其他vector操作
    《C++ Primer》学习笔记:向vector对象添加元素蕴含的编程假定
  • 原文地址:https://www.cnblogs.com/qichunlin/p/7932423.html
Copyright © 2020-2023  润新知