HDU 3927

    题意:构造一个N*N的矩阵,使得每行之和、每列之和、两个对角线上数字之和都不同。

    解法:推出来的(http://w3.math.sinica.edu.tw/math_media/d164/16419.pdf)都不是好孩子,随机算法才是王道。在比赛的时候不可能有那么多时间推,也许也推不出来。随机算法方法:首先随便构造一个矩阵,可能有多行、多列或者对角线之和相同。然后每次随机选取矩阵中的两个数字,试着交换它们,如果交换后每行、每列、对角线的不同的和的数字增加,则保持交换,否则交换回来。重复这一过程直至所有数字之和都不同。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <memory.h>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <algorithm>
#include <iostream>

#define ll long long

using namespace std;

const int N = 200;

int mtx[N][N], n;
int row[N], col[N], dia[2];
map<int, int> M;
int cunt;

void Change(int r1, int c1, int r2, int c2) {
    int d = mtx[r1][c1] - mtx[r2][c2];
    if (d == 0) return;
    
    int res = 0;
    if ((M[row[r1]]--) == 1) res--;
    if ((M[col[c1]]--) == 1) res--;
    if ((M[row[r2]]--) == 1) res--;
    if ((M[col[c2]]--) == 1) res--;
    if (r1 == c1 && (M[dia[0]]--) == 1) res--;
    if (r1 == n - c1 - 1 && (M[dia[1]]--) == 1) res--;
    if (r2 == c2 && (M[dia[0]]--) == 1) res--;
    if (r2 == n - c2 - 1 && (M[dia[1]]--) == 1) res--;
    
    swap(mtx[r1][c1], mtx[r2][c2]);
    row[r1] -= d; col[c1] -= d; row[r2] += d; col[c2] += d;
    if (r1 == c1) dia[0] -= d;
    if (r1 == n - c1 - 1) dia[1] -= d;
    if (r2 == c2) dia[0] += d;
    if (r2 == n - c2 - 1) dia[1] += d;

    if ((M[row[r1]]++) == 0) res++;
    if ((M[col[c1]]++) == 0) res++;
    if ((M[row[r2]]++) == 0) res++;
    if ((M[col[c2]]++) == 0) res++;
    if (r1 == c1 && (M[dia[0]]++) == 0) res++;
    if (r1 == n - c1 - 1 && (M[dia[1]]++) == 0) res++;
    if (r2 == c2 && (M[dia[0]]++) == 0) res++;
    if (r2 == n - c2 - 1 && (M[dia[1]]++) == 0) res++;

    if (res >= 0) {
        cunt += res;
        return;
    }
    
    M[row[r1]]--; M[col[c1]]--; M[row[r2]]--; M[col[c2]]--;
    if (r1 == c1) M[dia[0]]--;
    if (r1 == n - c1 - 1) M[dia[1]]--;
    if (r2 == c2) M[dia[0]]--;
    if (r2 == n - c2 - 1) M[dia[1]]--;
    
    swap(mtx[r1][c1], mtx[r2][c2]);
    row[r1] += d; col[c1] += d; row[r2] -= d; col[c2] -= d;
    if (r1 == c1) dia[0] += d;
    if (r1 == n - c1 - 1) dia[1] += d;
    if (r2 == c2) dia[0] -= d;
    if (r2 == n - c2 - 1) dia[1] -= d;

    M[row[r1]]++; M[col[c1]]++; M[row[r2]]++; M[col[c2]]++;
    if (r1 == c1)  M[dia[0]]++;
    if (r1 == n - c1 - 1) M[dia[1]]++;
    if (r2 == c2) M[dia[0]]++;
    if (r2 == n - c2 - 1) M[dia[1]]++;
}

int main() {
    int T, C, m, idx, s1, s2;
    int r1, c1, r2, c2;
    scanf("%d", &T);
    for (C = 1; C <= T; C++) {
        scanf("%d", &n);
        idx = 0;
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                mtx[i][j] = ++idx;
        M.clear();
        cunt = 0;
        for (int i = 0; i < n; i++) {
            s1 = 0, s2 = 0;
            for (int j = 0; j < n; j++) {
                s1 += mtx[i][j];
                s2 += mtx[j][i];
            }
            row[i] = s1; col[i] = s2;
            if ((M[s1]++) == 0) cunt++;
            if ((M[s2]++) == 0) cunt++;
        }
        s1 = s2 = 0;
        for (int i = 0; i < n; i++) {
            s1 += mtx[i][i];
            s2 += mtx[i][n-i-1];
        }
        dia[0] = s1; dia[1] = s2;
        if ((M[s1]++) == 0) cunt++;
        if ((M[s2]++) == 0) cunt++;
        m = 2 * n + 2;
        while (cunt < m) {
            r1 = rand() % n;
            r2 = rand() % n;
            c1 = rand() % n;
            c2 = rand() % n;
            
            Change(r1, c1, r2, c2);
        }
        printf("Case #%d:\n", C);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (j) printf(" ");
                printf("%d", mtx[i][j]);
            }
            printf("\n");
        }
        /*
        for (int i = 0; i < n; i++) {
            s1 = s2 = 0;
            for (int j = 0; j < n; j++) {
                s1 += mtx[i][j];
                s2 += mtx[j][i];
            }
            printf("%d %d ", s1, s2);
        }
        s1 = s2 = 0;
        for (int i = 0; i < n; i++) {
            s1 += mtx[i][i];
            s2 += mtx[i][n-i-1];
        }
        printf("%d %d\n", s1, s2);*/
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值