CodeForce 906B

题目链接:A - Seating of Students


思路:这个题按道理说是搜索。。。但是,我太菜了不会。。。 T_T(求大佬指点

好吧。。。说重点:我们看到数据不能用二维数组来存,只好用一维数组来存了(简单嘛)

然后我们发现,对于2 * 4 来说,

1 2 3 4     我们可以先  5 2 7 4

5 6 7 8                        1 6 3 8  ( 这样每一行的两个元素就都不同了 

然后再处理列。。

我们发现如果第一行(实际上就是奇数行。。。)所有元素右移(或者左移)一次的话,是不行的。移动两次是可以的

即  7 4 5 2

     1 6 3 8   处理完之后就可以保证现在相邻的两个元素之前肯定不相邻了!!!

类比到对于多行多列

我们首先将奇数列的每一个元素上移一位(最后一行由第一行补上),偶数列不变。这样就可以保证每一行中两个元素之前都不相邻!

然后,我们再将所有奇数行右移两个单位,偶数行不变!待处理完之后就是数据大的时候的答案


那么什么是数据小呢?(取 n < m (n>m同理

我们可以通过观察发现对于 n == 1  m <= 3 且 m != 1   和 n == 2  m <= 3 时 显然是不可以的

对于 m > 4  我们可以通过从小到大先输出奇数,再输出偶数得到解

对于 1 * 1 和 3 * 3 要进行特判一下!

1 * 1 时 只有一个元素输出即可

3 * 3 时按照上述的证明方式不存在的,但是实际上是存在的

3 * 3 时    6 1 8

                7 5 3

                2 9 4 ( 这个矩阵时可以的!!!WA了这个点上了!

对于 n > m 类似方法即可!

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[100005];
int b[100005];
int f1[100005];
int f2[100005];
int n,m;
bool flag = 0;
int main()
{
    scanf("%d%d",&n,&m);
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    memset(f1,0,sizeof(f1));
    memset(f2,0,sizeof(f2));
    for(int i=1; i<=n*m; i++)
        a[i] = b[i] = i;
    if(n == 1 && m == 1)
    printf("YES\n1\n");
    else if(n == 3 && m == 3)
    {
    	printf("YES\n");
        printf("6 1 8\n7 5 3\n2 9 4\n");
    }
    else if(n <= 3 && m <3 || m <= 3 && n < 3)
    {
        printf("NO\n");
    }
    else if(n == 1 && m > 3)
    {
        if(m == 4)
        {
            printf("YES\n");
            printf("3 1 4 2\n");
        }
        else
        {
            printf("YES\n");
            for(int i=1; i<=m; i+=2)
                printf("%d ",a[i]);
            for(int i=2; i<=m; i+=2)
                printf("%d ",a[i]);
            printf("\n");
        }
    }
    else if(m == 1 && n > 3)
    {
        if(n == 4)
        {
            printf("YES\n");
            printf("3\n1\n4\n2\n");
        }
        else
        {
            printf("YES\n");
            for(int i=1; i<=n; i+=2)
            	printf("%d\n",a[i]);
            for(int i=2; i<=n; i+=2)
                printf("%d\n",a[i]);
            printf("\n");
        }
    }
    else if(n <= m)
    {
        for(int i=1; i<=m*n-m; i+=2)
        {
            if((i-2)%m == 0)
                i--;
            a[i] = a[i+m];
        }
        int p = 1;
        for(int i=n*m-m+1; i<=n*m; i+=2)
        {
            a[i] = b[p];
            p += 2;
        }
        for(int i=1; i<n; i+=2)
        {
            int f1 = a[(i+1)*m-1];
            int f2 = a[(i+1)*m];
            for(int j=(i+1)*m; j>=i*m+1; j--)
            {
                a[j] = a[j-2];
            }
            a[i*m+1] = f1;
            a[i*m+2] = f2;
        }
        printf("YES\n");
        for(int j = 1; j<=n*m; j++)
        {
               if((j-1)%m==0&&j!=1)
                   printf("\n");
        		printf("%d ",a[j]);
        }
        printf("\n");
    }
    else if(n > m)
    {
    	for(int i=0; i<n; i+=2)
        {
        	int f1 = a[(i+1)*m];
            for(int j = (i+1)*m;j >= i*m+2;j--)
            {
            	a[j] = a[j-1];
            }
            a[i*m+1] = f1;
        }
			int p = 0;
		for(int i = 1;i<=m;i+=2)
			f1[++p] = a[i]; 
			
			p = 0;
		for(int i = m+1;i<=2*m;i+=2)
			f2[++p] = a[i];
        for(int i=1;i<=n*m-2*m;i+=2)
        {
        	a[i] = a[i+2*m];
        	if(m % 2 == 1 && (i)%m == 0)
        		i--;
        }
        p = 0;
        for(int i=n*m-2*m+1;i<=n*m-m;i+=2)
        {
        	a[i] = f1[++p];
        }
        p = 0;
        for(int i=n*m-m+1;i<=n*m;i+=2)
        {
        	a[i] = f2[++p];
        }
        printf("YES\n");
        for(int i=1;i<=n*m;i++)
        {
        	if((i-1)%m == 0&& i != 1)
        	printf("\n");
        	printf("%d ",a[i]);
		}
		printf("\n");
    }
    return 0;
}

好吧,其实还有一种思路:将 1 到 n*m 中的奇数和偶数分别找出来!

比如 1 2 3 4   --> 1 3 5 7  或者   3  1  4  2   奇数行前 m/2 列是奇数  后 m / 2 列是偶数

        5 6 7 8   --> 8 6 4 2            6  8  5  7    偶数行前 m/2 列是偶数    后 m/2 列是奇数

这种方法实现起来应该要比上面的方法代码量要小很多!!!

可以参考这篇博客


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值