2021-09-21

R13解题报告

上一次是多少来着?

R8?!!

哼,哼,啊啊啊啊啊啊啊啊啊啊啊啊!

T1
问题 A: 营救(save)

时间限制: 1 Sec 内存限制: 128 MB

题目描述

铁塔尼号遇险了!他发出了求救信号。距离最近的哥伦比亚号收到了讯息,时间就是生命,必须尽快赶到那里。
通过侦测,哥伦比亚号获取了一张海洋图。这张图将海洋部分分化成 n*n 个比较小的单位,其中用 1 标明的是陆地,用 0 标明是海洋。船只能从一个格子,移到相邻的四个格子。
为了尽快赶到出事地点,哥伦比亚号最少需要走多远的距离。

输入

第一行为 n,下面是一个 n*n 的 0、1 矩阵,表示海洋地图。
最后一行为四个小于 n 的整数,分别表示哥伦比亚号和铁塔尼号的位置。

输出

哥伦比亚号到铁塔尼号的最短距离,答案精确到整数。

样例输入

3
001
101
100
1 1 3 3

样例输出

4

N<=1000

宽搜版,打的时候注意死抓逻辑

  1. (分享一个理解:如果能保证自己算法的逻辑性,又能保证自己的实现的逻辑性,又能保证实现完全反映了算法,就可以保证整体正确性)
  2. (一点要在打的时候把逻辑抓死,否则重构不如重写)
  3. (dfs老1分没有了)
//environment
struct nod{
    int l;
    int c;
    int t;
    nod(int li = 0, int ci = 0, int ti = 0): l(li), c(ci), t(ti){}
};

int dl[]= {0, 0, 0, 1, -1};
int dc[]= {0, 1, -1, 0, 0};

inline void in(int &x)
{ while((x = getchar()) != '1' && x != '0') ; }

nod mov(nod a, int drc)
{ return nod(a.l + dl[drc], a.c + dc[drc], a.t + 1); }

bool operator==(nod a, nod b)
{ return a.l == b.l && a.c == b.c; }

	for(int i = 1 ; i <= n ; ++i)
		v[0][i] = v[n + 1][i] = v[i][n + 1] = v[i][0] = 1;
//bfs
    q.push(s);
    while(q.size())
    {
        x = q.front(), q.pop();
        if(v[x.l][x.c] || a[x.l][x.c] != '0') continue;
        v[x.l][x.c] = 1;
        for(int i = 1 ; i <= 4 ; ++i)
        {
            y = mov(x, i);
            if(v[y.l][y.c] || a[y.l][y.c] != '0') continue;
            if(y == e)
                return 0 & printf("%d", y.t);

            q.push(y);
        }
    }
T2
问题 B: 细胞(cell)

时间限制: 1 Sec 内存限制: 128 MB

题目描述

一矩形阵列由数字0到9组成,数字1到9代表细胞,细胞的定义为沿细胞数字上下左右还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。

输入

第一行两个整数,之间用一个空格隔开,表示矩阵的行数m和列数n。

第二行到第m+1行为mxn数字矩阵,数字之间没有空格。

输出

一行一个整数,表示细胞个数。

样例输入

4 10
0234500067
1034560500
2045600671
0000000089

样例输出

4

宽搜版,打的时候注意死抓逻辑

其实同上

逻辑就是,找到一个未访问过的点,++ans,从它开始以宽搜扩散,标记所有它可以到达的点

//environment
struct nod
{
    int l, c;
    nod(int li = 0, int ci = 0): l(li), c(ci) {}
};
int dl[]= {0, 0, 0, 1, -1};
int dc[]= {0, 1, -1, 0, 0};
nod mov(nod a, int drc)
{
    return nod(a.l + dl[drc], a.c + dc[drc]);
}
//BFS
for(int i = 1 ; i <= n ; ++i)
    {
        for(int j = 1 ; j <= m ; ++j)
        {
            if(a[i][j])
            {
                ++ans;

                q.push(nod(i, j));

                while(q.size())
                {
                    x = q.front(), q.pop();

                    if(!a[x.l][x.c])
                        continue;

                    a[x.l][x.c] = 0;

                    for(int i = 1 ; i <= 4 ; ++i)
                    {
                        y = mov(x, i);

                        if(!a[y.l][y.c])
                            continue;

                        q.push(y);
                    }
                }
            }
        }
    }
T3
问题 C: 体积(volume)

时间限制: 1 Sec 内存限制: 64 MB

题目描述

给出n件物品,每件物品有一个体积Vi,求从中取出若干件物品能够组成的不同的体积和有多少种可能。例如,n=3,V=(1,3,4),那么输出6,6种不同体积和具体为1、3、4、5、7、8。

输入

第1行1个正整数,表示n。
第2行n个正整数,表示Vi,每两个数之间用一个空格隔开。

输出

一行一个数,表示不同的体积和有多少种可能。

样例输入

1 3 4

样例输出

6

n≤20,1≤Vi≤50

可行性背包,已经没有什么好解释的了,

看上一篇。

dp[0] = 1;
    for(int i = 1 ; i <= n ; ++i)
    {
        for(int j = s ; j >= a[i] ; --j)
        {
            dp[j] |= dp[j - a[i]];
        }
    }
T4
问题 D: 数的拆分(decompose)

时间限制: 1 Sec 内存限制: 64 MB

题目描述

输人一个整数n,输出n拆分成若干正整数和的所有方案,即n=S1+S2+…+Sk.的形式,且S1≤S2≤…Sk,n≤20,请按照字典序输出。

输入

一行一个整数n。

输出

所有拆分方案,具体格式参见输出样例。

样例输入

4

样例输出

1+1+1+1
1+1+2
1+3
2+2
4
total=5

n <= 20

首先,对于任何一个数的分解,它的分解都可以等价于若干个(可为一个)和为它本身的数的分解

(而这一规则对于那些分解出来的数也成立)

即:递归

边界是零(分解完毕),输出搜索树上这根链上的所有节点。

加上几个细节(看代码注释)

void dfs(int k)
{
	if(k < 0) return;
	if(!k)
	{
		for(int i = 1 ; i <= t - 1 ; ++i)
		printf("%d+",s[i]);
		printf("%d\n",s[t]);
		++myTotal;
	}
    for(int i = max(s[t], 1) ; i <= k ; ++i)//下界是上一个的值,或1(意义值)(取交集)
    {
    	s[++t] = i;
    	dfs(k - i);
    	--t;
	}
}
T5
问题 E: 全排列问题(form)

题目描述

输出自然数 1 到 n 所有不重复的排列,即 n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。

输入

一行一个整数n(1≤n≤9)。

输出

由1~n 组成的所有不重复的数字序列,每行一个序列,一个数占5格。

样例输入

3

样例输出

    1    2    3
    1    3    2
    2    1    3
    2    3    1
    3    1    2
    3    2    1

水,配合深搜版理解。

void dfs(int k)
{
    if(k == n + 1)//若完成
    {
        for(int i = 1 ; i <= n ; ++i)
        {
            printf("%5d", a[i]);
        }
        putchar('\n');
    }
    for(int i = 1 ; i <= n ; ++i)//枚举状态转移
    {
        if(v[i])//去除不合理情况
            continue;
        v[i] = 1;
        a[k] = i;
        dfs(k + 1);
        v[i] = 0;//回溯
    }
}
T6
问题 F: 最佳调度问题

假设有n个任务由k个可并行工作的机器完成。完成任务i需要的时间为ti。试设计一个算法找出完成这n个任务的最佳调度,使得完成全部任务的时间最早。
对任意给定的整数n和k,以及完成任务i需要的时间为ti,i=1~n。编程计算完成这n个任务的最佳调度。

输入

第一行有2 个正整数n和k。第2 行的n个正整数是完成n个任务需要的时间。

n<=20 k<=20 ti<=100

输出

一行一个数,表示完成全部任务的最早时间。

样例输入

7 3
2 14 4 16 6 5 3

样例输出

17

爆搜,加一点点剪枝

(如果把搜索看成以一定规律,对于所有状态,把一个状态转移到另一个状态)

(其实剪枝就是及时减去不可能扩展出正确答案的状态)

(或者如果看成是在一个答案树上,以一定规律从父节点转移到子节点的话)

(那就是减去不可能存在着正确答案的枝条)

先sort一边
void dfs(int x, int ti)
{
    if(ti >= ans) return;
    if(x == n + 1)
    {
        ans = min(ans, ti);
        return;
    }
    for(int i = 1 ; i <= k ; ++i)
    {
        a[i] += w[x];
        dfs(x + 1, max(a[i], ti));
        a[i] -= w[x];
    }
}
bool cmp(int a, int b)
{
    return a>b;
}

T8

T9
问题 H: 金币问题II

题目描述

国王将金币作为工资,发放给忠诚的骑士。第一天,骑士收到一枚金币;之后两天(第二天和第三天),每天收到两枚金币;之后三天(第四、五、六天),每天收到三枚金币;之后四天(第七、八九、十天),每天收到四枚金币,…这种工资发放模式会-直这样延续下去。当连续N天每天收到N枚金币后,骑士会在之后的连续N+1天里,每天收到N+1枚金币。

请编程计算在从第一天开始的给定天数内,骑士一共获得了多少金币。

输入

输入包含至少一行,单不多于1000行

除最后一行外,输入的每行是一组输入数据,包含一个正整数n,表示天数。

输入的最后一行为0,表示输入结束。

输出

对每个数据输出一行一个整数,表示该数据对应的金币总数。

样例输入

10
6
7
11
15
16
100
10000
1000
21
22
0

样例输出

30
14
18
35
55
61
945
942820
29820
91
98

模拟,冷静细心即可,注意正确性。

啊,还有,是预处理。

for(xt i = 1 ; i <= sn ; ++i)
    {
        if(cur == lv)
        { ++lv, cur = 0; }
        ++cur;
        s[i] = s[i - 1] + lv;
    }

T10

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值