SD省队集训2019Day2之“大家佛”

大家佛( cut )

题目描述

提交答案题。

把一个矩阵划分成k个四连通块,使得包含的所有点的和最大的一个连通块的这个和与包含的所有点的和最小的一个连通块的这个和的差最小。

部分分(骗分)做法

A:手算

针对前几个规模较小的点可以这样做。

B:蛇形填

先求出每个连通块的和的平均值(\(\sum a_{ij}\div k\)),蛇形填的时候一旦超过这个值,就换下一个连通块。

#include <bits/stdc++.h>
using namespace std;
long long n, m, k, w, mat[1001][1001], id[1001][1001];
double every;
int main()
{
    char in[20], out[20];
    for (int i = 1, cnt; i <= 10; i++)
    {
        sprintf(in, "cut%d.in", i);
        sprintf(out, "cut%d.out", i);
        FILE *fin = fopen(in, "r"), *fout = fopen(out, "w");
        fscanf(fin, "%d%d%d%d", &n, &m, &k, &w);
        every = 0;
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                fscanf(fin, "%d", &mat[i][j]);
                every += (double)mat[i][j] / k;         //边加边除,防止全加起来以后会爆
            }
        }
        every = floor(every);
        long long now = 0, idd = 1;
        for (int i = 1; i <= n; i++)
        {
            if (i % 2)      //奇数行从左向右填
            {
                for (int j = 1; j <= m; j++)
                {
                    id[i][j] = idd;
                    now += mat[i][j];
                    if (idd < k && now >= every)//超过平均值就换下一组,但要注意处理最后一个连通块
                        idd++, now = 0;
                }
            }
            else        //偶数行从右向左填
            {
                for (int j = m; j >= 1; j--)
                {
                    id[i][j] = idd;
                    now += mat[i][j];
                    if (idd < k && now >= every)
                        idd++, now = 0;
                }
            }
        }
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                fprintf(fout, "%d ", id[i][j]);
            }
            fprintf(fout, "\n");
        }
    }
}

这个做法已经可以取得一定的分数。

C:数学方法

针对6、7两个点,矩阵里所有的数都是1。

以6号测试点为例,\(n=m=200,k=97\),此时可以求出平均值为\(200\times200\div97\approx412.37113\),那么我们不妨就用大小分别为412和413的连通块来填充。

设大小为412的连通块有\(x\)个,那么\(412x+413(97-x)=40000\),解得\(x=61\)。那么就可以先填61个大小为412的,再填36个大小为413的。

#include <bits/stdc++.h>
using namespace std;
long long n, m, k, w, mat[1001][1001], id[1001][1001];
int main()
{
    char in[20], out[20];
    for (int i = 6, cnt; i <= 6; i++)
    {
        sprintf(in, "cut%d.in", i);
        sprintf(out, "cut%d.out", i);
        FILE *fin = fopen(in, "r"), *fout = fopen(out, "w");
        fscanf(fin, "%d%d%d%d", &n, &m, &k, &w);
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                fscanf(fin, "%d", &mat[i][j]);
            }
        }
        long long now = 0, idd = 1;
        for (int i = 1; i <= n; i++)
        {
            if (i % 2)
            {
                for (int j = 1; j <= m; j++)
                {
                    id[i][j] = idd;
                    if (idd <= 61)
                    {
                        if (now == 412)
                        {
                            idd++;
                            now = 0;
                        }
                    }
                    else
                    {
                        if (now == 413)
                        {
                            idd++;
                            now = 0;
                        }
                    }
                    now++;
                }
            }
            else
            {
                for (int j = m; j >= 1; j--)
                {
                    id[i][j] = idd;
                    if (idd <= 61)
                    {
                        if (now == 412)
                        {
                            idd++;
                            now = 0;
                        }
                    }
                    else
                    {
                        if (now == 413)
                        {
                            idd++;
                            now = 0;
                        }
                    }
                    now++;
                }
            }
        }
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                fprintf(fout, "%d ", id[i][j]);
            }
            fprintf(fout, "\n");
        }
    }
}

7号同理。

#include <bits/stdc++.h>
using namespace std;
long long n, m, k, w, mat[1001][1001], id[1001][1001];
int main()
{
    char in[20], out[20];
    for (int i = 7, cnt; i <= 7; i++)
    {
        sprintf(in, "cut%d.in", i);
        sprintf(out, "cut%d.out", i);
        FILE *fin = fopen(in, "r"), *fout = fopen(out, "w");
        fscanf(fin, "%d%d%d%d", &n, &m, &k, &w);
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                fscanf(fin, "%d", &mat[i][j]);
            }
        }
        long long now = 0, idd = 1;
        for (int i = 1; i <= n; i++)
        {
            if (i % 2)
            {
                for (int j = 1; j <= m; j++)
                {
                    id[i][j] = idd;
                    if (idd <= 110)
                    {
                        if (now == 314)
                        {
                            idd++;
                            now = 0;
                        }
                    }
                    else
                    {
                        if (now == 315)
                        {
                            idd++;
                            now = 0;
                        }
                    }
                    now++;
                }
            }
            else
            {
                for (int j = m; j >= 1; j--)
                {
                    id[i][j] = idd;
                    if (idd <= 110)
                    {
                        if (now == 314)
                        {
                            idd++;
                            now = 0;
                        }
                    }
                    else
                    {
                        if (now == 315)
                        {
                            idd++;
                            now = 0;
                        }
                    }
                    now++;
                }
            }
        }
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                fprintf(fout, "%d ", id[i][j]);
            }
            fprintf(fout, "\n");
        }
    }
}
D:平均撕

每次把这个矩阵撕成两部分尽量平均的,撕的过程中使用随机退火之类算法。但是这个算法在“密度较大”(即nm小K大)矩阵上表现的很优秀,但是密度越小的矩阵就越不优秀。

E:半随机

先预定这些块的样子,然后在这些块之间使用DP,随机退火,指数枚举等方法找到最优解。比如可以将矩阵分成网格状的K块,或者是长条形的绕起来。这些方法也能得到不错的分数。

转载于:https://www.cnblogs.com/water-lift/p/10993758.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值