[比赛|考试]9.21上午考试
9.21考试
290/300(100,100,90)-rank2
缩索大乐斗
T了一个点(被教练dui了),以后练练卡常技巧
其实我感觉(经历了好几次低分之后)我对自己的要求降低了???
升降梯
不要问我这个名字为什么这么奇怪。 Nescafe 之塔一共有 N 层,升降梯在每层都有一个停靠点。手柄有 M 个控制 槽,第 i 个控制槽旁边标着一个数 Ci,满足 C1<C2<C3<……<CM。如果 Ci>0, 表示手柄扳动到该槽时,电梯将上升 Ci 层;如果 Ci<0,表示手柄扳动到该槽 时,电梯将下降-Ci 层;并且一定存在一个 Ci=0,手柄最初就位于此槽中。注 意升降梯只能在 1~N 层间移动,因此扳动到使升降梯移动到 1 层以下、 N 层以 上的控制槽是不允许的。 电梯每移动一层,需要花费 2 秒钟时间,而手柄从一个控制槽扳到相邻的槽, 需要花费 1 秒钟时间。探险队员现在在 1 层,并且想尽快到达 N 层,他们想知 道从 1 层到 N 层至少需要多长时间?
输入第一行两个正整数 N、 M。 第二行 M 个整数 C1、 C2……CM。 输出一个整数表示答案,即至少需要多长时间。若不可能到达输出-1。
对于 30% 的数据,满足 1≤N≤10, 2<=M<=5。 对于 100% 的数据,满足 1≤N≤1000, 2<=M<=20, -N<C1<C2<……<CM<N
Solution:最短路,把电梯在某一楼层且控制杆在某一位置的状态作为点,状态之间的转移作为边。每一个点有三种转移:操作杆左移,右移,电梯移动。直接dij即可(比SPFA快,tmd这题竟然不卡spfa)
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
struct fuck
{
int d, x, y;
//constructor
fuck(int d = 0, int x = 0, int y = 0) : d(d), x(x), y(y) {}
};
bool operator>(const fuck &a, const fuck &b)
{
return a.d > b.d;
}
int N, M, d[1010][25], c[25], src;
bool v[1010][25];
int main()
{
freopen("elevator.in", "r", stdin);
freopen("elevator.out", "w", stdout);
scanf("%d%d", &N, &M);
for (int i = 1; i <= M; i++)
{
scanf("%d", &c[i]);
if (c[i] == 0)
src = i;
}
priority_queue<fuck, vector<fuck>, greater<fuck> >q;
q.push(fuck(0, 1, src));
memset(d, 0x3f, sizeof(d));//????
d[1][src] = 0;
while (!q.empty())
{
int x = q.top().x, y = q.top().y;
q.pop();
if (v[x][y] == true)
continue;
v[x][y] = true;
if (y != 1)//往下扳
{
int newy = y - 1;
if (v[x][newy] == false && d[x][y] + 1 < d[x][newy])
{
d[x][newy] = d[x][y] + 1;
q.push(fuck(d[x][newy], x, newy));
}
}
if (y != M)//往上扳
{
int newy = y + 1;
if (v[x][newy] == false && d[x][y] + 1 < d[x][newy])
{
d[x][newy] = d[x][y] + 1;
q.push(fuck(d[x][newy], x, newy));
}
}
int newx = x + c[y];
if (newx >= 1 && newx <= N)
{
int dis = abs(c[y]) * 2;
if (v[newx][y] == false && d[x][y] + dis < d[newx][y])
{
d[newx][y] = d[x][y] + dis;
q.push(fuck(d[newx][y], newx, y));
}
}
}
int ans = 0x3f3f3f3f;
for (int i = 1; i <= M; i++)
{
if (d[N][i] < ans)
ans = d[N][i];
}
if (ans == 0x3f3f3f3f)
printf("-1\n");
else
printf("%d\n", ans);
fclose(stdin);
fclose(stdout);
return 0;
}
ps:一开始莫名其妙看错题了,以为每次掰杆会自动回复原位,然后就成了lg那道奇怪的电梯了
买鱼
王博退役后开始养鱼。他一早起床就赶去动物园,发现这个世界 的鱼真不少,五光十色、色彩斑斓,大的、小的,什么都有。这些鱼实在是太美 了,买的人越来越多,湖里的鱼越来越少。没有美丽的鱼哪里有美丽的湖?于是动 物园不得不规定,对于每种鱼,每个人最多可以买一条。并且有些鱼不能一起买 的,因为它们之间会相互争斗吞食。 王博想买尽可能多的鱼,但很可惜,他的资金有限。他冥想苦思,不知道如何 是好。请编写一个程序帮助他。如果有多个方案都能买尽可能多的鱼,选择所花 资金最多的一个
输入:从输入文件读入数据。输入文件的第一行为两个正整数 M(M≤l000),N(≤ 30),分别表示 王伯的资金和鱼的种类。以下 N 行,每行有两个正整数 s(1≤S ≤N),T,分别表示某种鱼的编号以及该鱼的价格。 接着,每行有两个正整数 P,Q。当 P,Q 均大于 0 时,表示 P,Q 不能共处;当 P,Q 均等于 0 时,表示输入文件的结束。
输出:输入文件的第一行为两个正整数 X,Y,分别表示所买的鱼的条数和总花费。 以下 X 行,每行有一个正整数,表示所买的鱼的编号。编号按升序排列输出。 如果题目有多个解,输出其中字典序最小的一个。
对于 100%的数据 M≤l000,N≤30
Solution:搜索。xjb乱搜。这种题尽量按照鱼的顺序搜,先把鱼按照编号排序,每次搜鱼,判断是否能够选择,如果可以就把后面冲突的鱼ban++,往下继续搜, 最后再ban回来。注意要先搜选择再搜不选择,这样第一个最优解就是答案。
然后注意可行剪枝:搜索过程中记录价格,限制小于等于M以防王博破产。最优剪枝:如果当前的鱼再买也买不到当前统计的答案那么多直接返回
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
struct fuck
{
int id, price;
} a[40];
map<int, int> getid;
int N, M, g[40][40], ban[40];
bool choose[40];
bool cmpid(const fuck &a, const fuck &b)
{
return a.id < b.id;
}
int maxcnt = 0, maxprice = 0, ans[40];
void search(int x, int totprice, int totcnt)
{
//最优性剪枝:如果当前再买也买不到当前最大答案那么多了直接返回
//可行性剪枝:价钱剪枝,保证王博不破产
if (totcnt + N - x + 1 < maxcnt)
return;
if (x == N + 1)
{
if (maxcnt < totcnt || (maxcnt == totcnt && maxprice < totprice))
{
maxcnt = totcnt;
maxprice = totprice;
for (int i = 1; i <= N; i++)
ans[i] = choose[i];
}
return;
}
//选择鱼的条件是鱼没有被禁止,并且价格够
if (ban[x] == 0 && totprice + a[x].price <= M)
{
//ban了其它鱼
choose[x] = true;
for (int i = x + 1; i <= N; i++)
if (g[x][i] == true)
ban[i]++;
//缩索
search(x + 1, totprice + a[x].price, totcnt + 1);
//ban回来
choose[x] = false;
for (int i = x + 1; i <= N; i++)
if (g[x][i] == true)
ban[i]--;
}
search(x + 1, totprice, totcnt);
}
int main()
{
freopen("fish.in", "r", stdin);
freopen("fish.out", "w", stdout);
scanf("%d%d", &M, &N);
for (int i = 1; i <= N; i++)
scanf("%d%d", &a[i].id, &a[i].price);
sort(a + 1, a + 1 + N, cmpid);
for (int i = 1; i <= N; i++)
getid[a[i].id] = i;
int p, q;
while (2333)
{
scanf("%d%d", &p, &q);
if (p == 0 && q == 0)
break;
p = getid[p];
q = getid[q];
g[p][q] = g[q][p] = true;
}
search(1, 0, 0);
printf("%d %d\n", maxcnt, maxprice);
for (int i = 1; i <= N; i++)
{
if (ans[i] == true)
printf("%d\n", a[i].id);
}
fclose(stdin);
fclose(stdout);
return 0;
}
登山(小猫爬山)
Freda 和 rainbow 饲养了 N 只小猫,这天,小猫们要去爬山。经历了千辛万 苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了
Freda 和 rainbow 只好花钱让它们坐索道下山。索道上的缆车最大承重量为 W,而 N 只小猫的重量分别是 C1、 C2……CN。当然,每辆缆车上的小猫的重量 之和不能超过 W。每租用一辆缆车, Freda 和 rainbow 就要付 1 美元,所以他们 想知道,最少需要付多少美元才能把这 N 只小猫都运送下山?
输入:第一行包含两个用空格隔开的整数, N 和 W。 接下来 N 行每行一个整数,其中第 i+1 行的整数表示第 i 只小猫的重量 Ci。
输出:输出一个整数,最少需要多少美元,也就是最少需要多少辆缆车。
数据范围:对于 100%的数据, 1<=N<=18, 1<=Ci<=W<=10^8
Source:Nescáfe 26杯赛 T1
Solution:首先不难想到一个暴力:枚举全排列,按照排列放猫,然而只有10分
然后呢考虑直接搜索,搜索每一只小猫放在哪个车里或者直接新开一个缆车。
最优形剪枝:如果当前缆车数目大于最小答案,直接return,为了发挥作用,我们需要先搜索把小猫放在原有的车里,再另开一辆车
优化:从大到小sort一遍,可以脑补优化的情形(你先放了大的肯定吼啊)
#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
int N, W, a[25], fuck[25], tot, ans = 0x3f3f3f3f;
void search(int pos)
{
if (pos == N + 1)
{
ans = min(ans, tot);
return;
}
for (int i = 1; i <= tot; i++)
{
if (fuck[i] + a[pos] <= W)
{
fuck[i] += a[pos];
search(pos + 1);
fuck[i] -= a[pos];
}
}
if (tot < ans)
{
tot++;
fuck[tot] = a[pos];
search(pos + 1);
tot--;
}
}
int main()
{
freopen("climb.in", "r", stdin);
freopen("climb.out", "w", stdout);
scanf("%d%d", &N, &W);
for (int i = 1; i <= N; i++)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + N, greater<int>());
search(1);
printf("%d\n", ans);
fclose(stdin);
fclose(stdout);
return 0;
}
orz GMPotlc AK大佬