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
宽搜版,打的时候注意死抓逻辑
- (分享一个理解:如果能保证自己算法的逻辑性,又能保证自己的实现的逻辑性,又能保证实现完全反映了算法,就可以保证整体正确性)
- (一点要在打的时候把逻辑抓死,否则重构不如重写)
- (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