题目一:金属采集
Description
人类在火星上发现了一种新的金属!这些金属分布在一些奇怪的地方,不妨叫它节点好了。一些节点之间有道路相连,所有的节点和道路形成了一棵树。一共有 n 个节点,这些节点被编号为 1~n 。人类将 k 个机器人送上了火星,目的是采集这些金属。这些机器人都被送到了一个指定的着落点, S 号节点。每个机器人在着落之后,必须沿着道路行走。当机器人到达一个节点时,它会采集这个节点蕴藏的所有金属矿。当机器人完成自己的任务之后,可以从任意一个节点返回地球。当然,回到地球的机器人就无法再到火星去了。我们已经提前测量出了每条道路的信息,包括它的两个端点 x 和 y,以及通过这条道路需要花费的能量 w 。我们想花费尽量少的能量采集所有节点的金属,这个任务就交给你了。
Input
第一行包含三个整数 n, S 和 k ,分别代表节点个数、着落点编号,和机器人个数。
接下来一共 n-1 行,每行描述一条道路。一行含有三个整数 x, y 和 w ,代表在 x 号节点和 y 号节点之间有一条道路,通过需要花费 w 个单位的能量。所有道路都可以双向通行。
Output
输出一个整数,代表采集所有节点的金属所需要的最少能量。
Sample Input 1
6 1 3 1 2 1 2 3 1 2 4 1000 2 5 1000 1 6 1000
Sample Output 1
3004
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxx=1e5+100;
struct edge{
int to,next,w;
}e[maxx<<1];
int dp[maxx][15],head[maxx<<1];
int n,s,k,tot;
inline void init()
{
memset(head,-1,sizeof(head));
memset(dp,0,sizeof(dp));
tot=0;
}
inline void add(int u,int v,int w)
{
e[tot].next=head[u],e[tot].to=v,e[tot].w=w,head[u]=tot++;
}
inline void dfs(int u,int f)
{
for(int i=head[u];i!=-1;i=e[i].next)
{
int to=e[i].to;
if(to==f) continue;
dfs(to,u);
for(int r=k;r>=0;r--)
{
dp[u][r]+=dp[to][0]+2*e[i].w;
for(int j=1;j<=r;j++) dp[u][r]=min(dp[u][r],dp[u][r-j]+dp[to][j]+j*e[i].w);
}
}
}
int main()
{
scanf("%d%d%d",&n,&s,&k);
init();
int x,y,z;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs(s,0);
printf("%d\n",dp[s][k]);
return 0;
}
题目二:铺地毯
Description
为了准备一个学生节,组织者在会场的一片矩形区域(可看做是平面直角坐标
系的第一象限)铺上一些矩形地毯。一共有n 张地毯,编号从1 到n。现在将这些地毯按照
编号从小到大的顺序平行于坐标轴先后铺设,后铺的地毯覆盖在前面已经铺好的地毯之上。
地毯铺设完成后,组织者想知道覆盖地面某个点的最上面的那张地毯的编号。注意:在矩形
地毯边界和四个顶点上的点也算被地毯覆盖。
Input
输入共 n+2 行。
第一行,一个整数 n,表示总共有n 张地毯。
接下来的 n 行中,第i+1 行表示编号i 的地毯的信息,包含四个正整数a,b,g,k,每
两个整数之间用一个空格隔开,分别表示铺设地毯的左下角的坐标(a,b)以及地毯在x
轴和y 轴方向的长度。
第 n+2 行包含两个正整数x 和y,表示所求的地面的点的坐标(x,y)。
Output
输出共 1 行,一个整数,表示所求的地毯的编号;若此处没有被地毯覆盖则输出-1。
Sample Input 1
3 1 0 2 3 0 2 3 3 2 1 3 3 2 2
Sample Output 1
3
思路:
这题,只要把所有的地毯信息都存入数组,然后遍历,如果地毯覆盖部分包含了目标点,则更新存储地毯编号的变量。
判断包含的方法:设任意地毯信息为a,b,g,k
,则其覆盖坐标为x:a < x < g + a
,y:b < y < k + b
。如此判断即可。
#include<iostream>
using namespace std;
const int N = 1e4 + 10;
int n;
int a, b, g, k;
int x, y;
struct loc { int a, b, g, k; };
loc gd[N];
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> a >> b >> g >> k;
gd[i] = {a, b, g, k};
}
cin >> x >> y;
int ret = -1;
for(int i = 1; i <= n; i ++)
{
if(x >= gd[i].a && x <= gd[i].g + gd[i].a)
if(y >= gd[i].b && y <= gd[i].k + gd[i].b)
ret = i;
}
cout << ret;
return 0;
}
题目三:扫雷
Description
扫雷游戏你一定玩过吧!现在给你若干个n×m的地雷阵,请你计算出每个矩阵中每个单元格相邻单元格内地雷的个数,每个单元格最多有8个相邻的单元格。 0<n,m<=100
Input
输入包含若干个矩阵,对于每个矩阵,第一行包含两个整数n和m,分别表示这个矩阵的行数和列数。接下来n行每行包含m个字符。安全区域用‘.’表示,有地雷区域用'*'表示。当n=m=0时输入结束。
Output
对于第i个矩阵,首先在单独的一行里打印序号:“Field #i:”,接下来的n行中,读入的'.'应被该位置周围的地雷数所代替。输出的每两个矩阵必须用一个空行隔开。
(注意两个矩阵之间应该有一个空行,由于oj的格式化这里不能显示出来)
Sample Input 1
4 4 *... .... .*.. .... 3 5 **... ..... .*... 0 0
Sample Output 1
Field #1: *100 2210 1*10 1110 Field #2: **100 33200 1*100
思路:
1.是否存储输入:不存储,边输入边处理。
2.怎么处理:二维数组初始化为全0,从下标(1,1)的位置开始存储数据,可有效避免数组越界。当读入了一个“地雷”时,即“*”,就直接暴力把周围8个格子中的非地雷格数量加1,同时地雷格子设定为"-1"。然后遍历输出数组,遇到-1,输出星号即可。
3.如何重新开始:重新开始时,要注意清空上一次使用过的数组信息。
#include<iostream>
#include<cstring>
using namespace std;
const int N = 150;
char ch;
int n, m, cnt;
int F[N][N];
int dx[4], dy[4];
int main()
{
while(cin >> n >> m)
{
if(n == 0 || m == 0)
break;
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j<= m; j ++)
{
cin >> ch;
if(ch == '*')
{
F[i][j] = -1;
if(F[i-1][j-1] != -1) F[i-1][j-1]++;
if(F[i-1][j] != -1) F[i-1][j]++;
if(F[i-1][j+1] != -1) F[i-1][j+1]++;
if(F[i][j-1] != -1) F[i][j-1]++;
if(F[i][j+1] != -1) F[i][j+1]++;
if(F[i+1][j-1] != -1) F[i+1][j-1]++;
if(F[i+1][j] != -1) F[i+1][j]++;
if(F[i+1][j+1] != -1) F[i+1][j+1]++;
}
}
}
printf("Field #%d:\n", ++cnt);
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= m; j ++)
{
if(F[i][j] == -1)
cout << "*";
else
cout << F[i][j];
}
cout << endl;
}
cout << endl;
memset(F, 0 ,sizeof F);
}
return 0;
}
题目四:去注释
Description
给你一段C++代码,将其中的注释去除后输出剩余的代码。
注释共有两种形式:
1. 行注视:以//开头,一直作用到行尾为止。
例子:
int n;//n表示数据规模
int a;
去注释后:
int n;
int a;
注意:保留行尾换行符
2. 段注视:以/*开头,到*//结尾,中间部分都是注释,可以跨行。
例子:
int main() {
/*
我是
一段
注释
*/
}
去注释后:
int main() {
}
注意:由于在线评测系统(Online Judge)对网页显示文本作了格式化,一些空行会被删去,导致上面显示的删除后的结果不正确。删除注释后,剩余的代码应该是三行,两行代码之间有一个空行。这是因为:在段注释结尾符的后面有一个换行符,它不在注释内,需要保留。
输入样例:
int main() {
/*
我是
一段
注释
*/
int n;//n表示数据规模
}
Input
一段C++程序代码
输出样例:
int main() {
int n;
}
注意:和之前题目中的解释一样,在int n;之前有一个空行,被在线评测系统删掉,实际程序输出应该有该空行。
Output
去掉注释部分后的程序
思路:
1.怎么读入数据:利用while((ch = cin.get()) != EOF)
即可判断输入是否结束。将输入的每一个字节都存入数组,在对数组进行操作。
2.怎么操作:两种方式,删除或者跳跃,删除太麻烦了。直接选择跳跃。
3.怎么跳越:两种注释都有特征。
#include<iostream>
using namespace std;
const int N = 1e5;
int pos;
char ch, code[N];
int main()
{
while((ch = cin.get()) != EOF)
code[pos++] = ch;
code[pos++] = ' ';
for(int i = 0; i < pos - 1; i ++)
{
if(code[i] == '/' && code[i + 1] == '*')
{
i += 2;
for(int j = i; j < pos; j ++)
if(code[j] == '*' && code[j + 1] == '/')
i = j, j = pos;
i += 2;
}
if(code[i] == '/' && code[i + 1] == '/')
{
i += 2;
while(code[i] != '\n')
i ++;
}
cout << code[i];
}
return 0;
}