挑战程序设计竞赛(课后题难题汇总)
1. AOJ 0033 Ball(贪心)
#include<cstdio>
#include<algorithm>
using namespace std;
int T, a[15];
void solve()
{
int x1 = a[0], x2 = -1e8;
for(int i = 1; i < 10; i++){
if(a[i] > x1 && a[i] > x2){
if(a[i] - x1 > a[i] - x2){
x2 = a[i];
}
else{
x1 = a[i];
}
}
else if(a[i] > x1){
x1 = a[i];
}
else if(a[i] > x2){
x2 = a[i];
}
else{
printf("NO\n");
return;
}
}
printf("YES\n");
}
int main()
{
scanf("%d", &T);
while(T--){
for(int i = 0; i < 10; i++){
scanf("%d", &a[i]);
}
solve();
}
return 0;
}
(1)无需用stack,因为我们只关心最上面的两个数,因此只需用x1, x2保存最上面的两个数即可。
(2)初始化x1应为a[0],x2应初始化为-INF,不能把x2初始化为a[1],应该是直到不能往x1那一摞放小球的时候才往x2放。
2.POJ 3009 Curling 2.0 (回溯法)
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int field[25][25], dx[5] = {1, -1, 0, 0}, dy[5] = {0, 0, 1, -1};
int N, M, res, res0, sx, sy, gx, gy;
void dfs(int x, int y)
{
if(res > 10){
return;
}
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
bool go = 0;
while(1 <= nx && nx <= N && 1 <= ny && ny <= M && field[nx][ny] != 1){
go = true;
if(nx == gx && ny == gy){
res0 = min(res, res0);
}
nx += dx[i], ny += dy[i];
}
if(field[nx][ny] == 1 && go){
field[nx][ny] = 0;
res++;
dfs(nx - dx[i], ny - dy[i]);
res--;
field[nx][ny] = 1;
}
}
}
int main()
{
while(scanf("%d%d", &M, &N) && N){
memset(field, 0, sizeof(field));
res = 1, res0 = 1e8;
for(int i = 1; i <= N; i++){
for(int j = 1; j <= M; j++){
scanf("%d", &field[i][j]);
if(field[i][j] == 2){
sx = i, sy = j;
}
else if(field[i][j] == 3){
gx = i, gy = j;
}
}
}
dfs(sx, sy);
if(res0 <= 10){
printf("%d\n", res0);
}
else{
printf("%d\n", -1);
}
}
return 0;
}
(1)注意这个数组下标从1开始,否则如果下标从0开始,如果一旦从左边或上边扔出墙外,下标会变为-1,然后会越界。
(2)field每组数据都一定要初始化为0,因为跑到墙外面怎么判断?一个是跳出while循环,另一个是在下面if(field[nx][ny] == 1 && go) 那里,如果不初始化,从右边和下边出了墙后,本来field对应的应该是0,但是却有可能是上一组数据的值,如果原本是1,就又进了if判断,导致出错。
3.AOJ 0558 Cheese
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int INF = 1e8;
int N, M, C, d[1005][1005], dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
typedef pair<int, int> P;
P cheese[15];
char field[1005][1005];
int bfs(int sx, int sy, int gx, int gy)
{
for(int i = 0; i < N; i++){
fill(d[i], d[i] + M, INF);
}
d[sx][sy] = 0;
queue<P> que;
que.push(P(sx, sy));
while(!que.empty()){
P p = que.front();
que.pop();
int x = p.first, y = p.second;
if(x == gx && y == gy){
return d[x][y];
}
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(0 <= nx && nx < N && 0 <= ny && ny < M && field[nx][ny] != 'X' && d[nx][ny] == INF){
que.push(P(nx, ny));
d[nx][ny] = d[x][y] + 1;
}
}
}
}
int main()
{
scanf("%d%d%d", &N, &M, &C);
for(int i = 0; i < N; i++){
scanf("%s", field[i]);
}
for(int i = 0; i < N; i++){
for(int j = 0; j < M; j++){
if(field[i][j] == 'S'){
cheese[0] = P(i, j);
}
else if('0' < field[i][j] && field[i][j] <= '9'){
cheese[field[i][j] - '0'] = P(i, j);
}
}
}
int res = 0;
for(int i = 1; i <= C; i++){
res += bfs(cheese[i - 1].first, cheese[i - 1].second, cheese[i].first, cheese[i].second);
}
printf("%d\n", res);
return 0;
}
(1)学会把问题划分为子问题。
(1)一定要注意!虽然没有用全局变量。全局变量一定要注意!不要在后面用的时候不要再int声明,不然会屏蔽掉外部变量!
(2)此题奶酪工厂是可以直接经过的,不是没吃过的工厂就就不让过。
4.POJ 3669 Meteor Shower
(1)这次又想了一个方法,设置一个field的二维数组,记录流星砸的最早时间,永远不砸就是INF。然后算人走的时间,用d保存,一旦有field[x][y] <= d[x][y],就把d[x][y]设为2 * INF。最后,遍历两个数组,寻找field[x][y] == INF 的地方对应的d[x][y]的最小值。个人感觉这个方法更简单一些。
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 305, M = 305, maxc = 50005, INF = 1e8;
int C, x[maxc], y[maxc], t[maxc], dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0};
int field[N][M], d[N][M];
typedef pair<int, int> P;
void solve()
{
for(int i = 0; i < N; i++){
for(int j = 0; j < M; j++){
field[i][j] = d[i][j] = INF;
}
}
for(int i = 0; i < C; i++){
int x1 = x[i], y1 = y[i];
//防止重复砸。注意是找最小值,因为不一定先输入的流星砸得就早。
field[x1][y1] = min(field[x1][y1], t[i]);
for(int j = 0; j < 4; j++){
int nx = x1 + dx[j], ny = y1 + dy[j];
if(0 <= nx && nx < N && 0 <= ny && ny < M){
field[nx][ny] = min(field[nx][ny], t[i]);
}
}
}
d[0][0] = 0;
queue<P> que;
que.push(P(0, 0));
while(!que.empty()){
P p = que.front();
que.pop();
int x = p.first, y = p.second;
if(d[x][y] >= field[x][y]){
d[x][y] = 2 * INF;
//切记,这里别设为INF,不然无法区分不能经过这个地方与还没有到这个地方两种情况。
continue;
}
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(0 <= nx && nx < N && 0 <= ny && ny < M && d[nx][ny] == INF){
que.push(P(nx, ny));
d[nx][ny] = d[x][y] + 1;
}
}
}
int res = INF;
for(int i = 0; i < N; i++){
for(int j = 0; j < M; j++){
if(field[i][j] == INF){
res = min(d[i][j], res);
}
}
}
if(res == INF){
printf("%d\n", -1);
}
else{
printf("%d\n", res);
}
}
int main()
{
scanf("%d", &C);
for(int i = 0; i < C; i++){
scanf("%d%d%d", &x[i], &y[i], &t[i]);
}
solve();
return 0;
}
kuangbin系列
1.HDU 2612 Find a way
(1)这道题遍历两边城市地图就可以了,否则会超时。
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int INF = 1e7;
int N, M, sx1, sy1, sx2, sy2;
int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};
char field[205][205];
int d[205][205];
typedef pair<int, int> P;
vector<P> kfc;
void bfs(int sx, int sy)
{
for(int i = 0; i < N; i++){
for(int j = 0; j < M; j++){
d[i][j] = INF;
}
}
d[sx][sy] = 0;
queue<P> que;
que.push(P(sx, sy));
while(!que.empty()){
P p = que.front();
que.pop();
int x = p.first, y = p.second;
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(0 <= nx && nx < N && 0 <= ny && ny < M && field[nx][ny] != '#' && d[nx][ny] == INF){
que.push(P(nx, ny));
d[nx][ny] = d[x][y] + 11;
}
}
}
}
void solve()
{
int sz = kfc.size();
vector<int> res1, res2;
bfs(sx1, sy1);
for(int i = 0; i < sz; i++){
int x = kfc[i].first, y = kfc[i].second;
res1.push_back(d[x][y]);
}
bfs(sx2, sy2);
for(int i = 0; i < sz; i++){
int x = kfc[i].first, y = kfc[i].second;
res2.push_back(d[x][y]);
}
int res = 1e9;
for(int i = 0; i < sz; i++){
res = min(res, res1[i] + res2[i]);
}
printf("%d\n", res);
}
int main()
{
while(scanf("%d%d", &N, &M) == 2){
kfc.clear();
for(int i = 0; i < N; i++){
scanf("%s", &field[i]);
}
for(int i = 0; i < N; i++){
for(int j = 0; j < M; j++){
if(field[i][j] == '@'){
kfc.push_back(P(i, j));
}
else if(field[i][j] == 'Y'){
sx1 = i, sy1 = j;
}
else if(field[i][j] == 'M'){
sx2 = i, sy2 = j;
}
}
}
solve();
}
return 0;
}
2.UVA 11624 Fire!
(1)这道题很恶,题目只说有一个J,但没说只有一个F,因此有可能有多个火源。
(2)一开始我的思路是先算人到边界时间,再算每个火到边界的最短时间,若人到边界的时间小于火到边界的时间,则可以逃脱。但是这种算法会超时。
(3)因此,只能把这个地图遍历一次,先把所有的火源推入队列,然后把人推入队列,这样表明火先走,人再走。只要火到的地方就变成墙。别忘一开始把火的位置变成墙。
WA,但并不知道是哪里错了。
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int INF = 1e8;
int T, N, M, dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
int d[1005][1005], sx, sy;
char field[1005][1005];
struct P
{
int x, y;
bool kind; //0 indicates John, and 1 indicates the fire;
};
vector<P> fire;
int bfs()
{
for(int i = 0; i < N; i++){
for(int j = 0; j < M; j++){
d[i][j] = INF;
}
}
d[sx][sy] = 0;
queue<P> que;
int sz = fire.size();
for(int i = 0; i < sz; i++){
que.push(fire[i]);
}
que.push({sx, sy, false});
while(!que.empty()){
P p = que.front();
que.pop();
int x = p.x, y = p.y;
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(0 <= nx && nx < N && 0 <= ny && ny < M && field[nx][ny] != '#' && d[nx][ny] == INF){
if(p.kind){
field[nx][ny] = '#';
que.push({nx, ny, true});
}
else{
que.push({nx, ny, false});
d[nx][ny] = d[x][y] + 1;
}
}
}
}
int res = INF;
for(int i = 0; i < N; i++){
res = min(res, d[i][0]);
res = min(res, d[i][M - 1]);
}
for(int j = 0; j < M; j++){
res = min(res, d[0][j]);
res = min(res, d[N - 1][j]);
}
return res + 1;
}
void solve()
{
for(int i = 0; i < N; i++){
for(int j = 0; j < M; j++){
if(field[i][j] == 'J'){
sx = i, sy = j;
}
else if(field[i][j] == 'F'){
fire.push_back({i, j, true});
field[i][j] = '#';
}
}
}
int res = bfs();
if(res >= INF){
printf("IMPOSSIBLE\n");
}
else{
printf("%d\n", res);
}
}
int main()
{
scanf("%d", &T);
while(T--){
scanf("%d%d", &N, &M);
for(int i = 0; i < N; i++){
scanf("%s", field[i]);
}
solve();
}
return 0;
}
cf
1.D. Solve The Maze
这道题我的一开始思路是把所有坏人的四个方向判断一下,若有好人,则结束并输出No,否则换成‘#’。然后把所有连成一起的好人换成‘.’(用深搜),这样可以减少搜索数量。最后用广搜看看每个好人到终点的距离是否为INF。