一.题目解法与WA分析
1.题G.巧妙解法
题目描述:
有400个房间,门是对着的:
现在要将若干个房间中的椅子搬运。
由于狭道太窄了,一次只能搬运一张桌子。搬运一张桌子需要10分钟,而且情况如下:
求最短搬运时间。
样例输入:
3
4
10 20
30 40
50 60
70 80
2
1 3
2 200
3
10 100
20 80
30 50
输出:
10
20
30
思路分析:
这里运用到了一个非常巧妙的方法。把1、2房间看成一个走廊,3、4房间看成一个走廊…
一共200个走廊。我们只记下走过次数最多的走廊(即是轮数)乘10即得。
实现代码:
#include <iostream>
using namespace std;
int main(){
int i, m, n, k, j;
cin >> m;
while(m--){
cin >> j;
int zoulang[200] = { 0 };
//不可写成zoulang[200] = { 0 },否则只是对第201个元素赋值
while(j--){
cin >> n >> k;
if(n > k) { i = n; n = k; k = i; }
for(i = (n + 1) / 2; i <= (k + 1) / 2; i++) zoulang[i]++;
}
int count = 0;
for(i = 0; i < 200; i++)
if(count < zoulang[i])
count = zoulang[i];
cout << count * 10 << endl;
}
return 0;
}
2.J题WA分析
题目描述:
小明做作业,一共有 n 门。
每门有期限,超限没学分。
学分各不同,期限会重叠。
实在做不完,最低减多少分?
样例输入:
输入t组,然后输入作业数,接下来是作业时间n([0, n])和学分。
3
3
3 3 3
10 5 1
3
1 3 1
6 2 3
7
1 4 6 4 2 4 3
3 2 1 7 6 5 4
输出:
最低减分。
0
3
5
WA分析:
WA大意:因为既有时间限制,又有学分权重。这时把时间作为第一权重降序,学分作为第二权重升序。然后遍历。
这样做是不行的,因为有时候出现时间短学分少却在权重比较中“压没”时间长学分多的。
于是,可以尝试向struct添加一个flag,表示有没有被遍历选中。这样,再遇到不得不扣学分项时,可以看看前面有没有没选过而且扣得少的来抵押。
代码实现:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
struct node{
int t;
int s;
bool flag;
}pool[10010];
bool cmp(node a, node b){
if(a.t != b.t) return a.t < b.t;
else return a.s > b.s;
}
int main() {
int i, t, n, s, count;
cin >> t;
while(t--){
cin >> s;
for (i = 0; i < s; i++) cin >> pool[i].t;
for (i = 0; i < s; i++) { cin >> pool[i].s; pool[i].flag = false; }
sort(pool, pool + s, cmp);
int sum = 0, k = 1;
for (i = 0; i < s; i++){
if(pool[i].t >= k){ k++;continue; }
int pos = i, tmp = pool[i].s;
for (n = 0; n < i; n++){
if(pool[n].s < tmp && !pool[n].flag){
tmp = pool[n].s;
pos = n;
}
}
sum += tmp;
pool[pos].flag = true;
}
cout << sum << endl;
}
return 0;
}
3.T题WA分析
题目大意:
整数列组[a, b], a 和 b 啥的都是整数。
找出两个不同数,每个区间都含有。
所以最少几个数。
WA分析:
WA解法:设置区间块为第一个区间首位和末位。如果重叠,首位不动,末位到第二个区间末位且自增2。
正确解法:因为是两个整数,所以我们创建一个“区间块”[m, n],其中m n都是整数,且m n相邻。这是我们只要找到区间重合部分的“区间块”即可。
设置计数器初始值是1,区间块初始为第一个区间末二位。
如果没有重叠,即“区间和”是0,则自增2,且区间块和蹦到下一个区间最后;
如果重叠一个数,即区间块2为半个,则自增一蹦区间和;
如果重叠两个,不增,区间块蹦。
代码实现:
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
struct node
{
int a, b;
} pool[10010];
bool cmp(node a, node b)
{
return a.b < b.b;
}
int main()
{
int n, i, a_axis, b_axis, count;
while (scanf("%d", &n) != EOF){
count = 2;
for (i = 0; i < n; i++)
cin >> pool[i].a >> pool[i].b;
sort(pool, pool + n, cmp);
a_axis = pool[0].b - 1;
b_axis = pool[0].b;
for(i = 1; i < n; i++){
if(pool[i].b >= b_axis && pool[i].a <= a_axis) continue;
if (pool[i].a > b_axis){
a_axis = pool[i].b - 1;
b_axis = pool[i].b;
count += 2;
}else{
a_axis = b_axis;
b_axis = pool[i].b;
count++;
}
}
cout << count << endl;
}
return 0;
}
二.总结
这周学的不算多,主要以做题为主。在做题过程中发现了许多问题可以用极其简单的方法去做,有些题目需要考虑多种情况。在遇到这些题目时,只是看是不可以的,需要动手在纸上画画写写才能得出算法。
这周打CF的感觉和打OpenJudge、vjudge都不一样。CF比vjudge难了一些,因为算法不只局限于贪心,相比OpenJudge更是天上和地下的差别。以后要多在CF训练场训练。