[NOIP1996 提高组] 挖地雷
题目描述
在一个地图上有 N ( N ≤ 20 ) N\ (N \le 20) N (N≤20) 个地窖,每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径。当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。
输入格式
有若干行。
第 1 1 1 行只有一个数字,表示地窖的个数 N N N。
第 2 2 2 行有 N N N 个数,分别表示每个地窖中的地雷个数。
第 3 3 3 行至第 N + 1 N+1 N+1 行表示地窖之间的连接情况:
第 3 3 3 行有 n − 1 n-1 n−1 个数( 0 0 0 或 1 1 1),表示第一个地窖至第 2 2 2 个、第 3 3 3 个 … \dots … 第 n n n 个地窖有否路径连接。如第 3 3 3 行为 11000 ⋯ 0 11000\cdots 0 11000⋯0,则表示第 1 1 1 个地窖至第 2 2 2 个地窖有路径,至第 3 3 3 个地窖有路径,至第 4 4 4 个地窖、第 5 5 5 个 … \dots … 第 n n n 个地窖没有路径。
第 4 4 4 行有 n − 2 n-2 n−2 个数,表示第二个地窖至第 3 3 3 个、第 4 4 4 个 … \dots … 第 n n n 个地窖有否路径连接。
……
第 n + 1 n+1 n+1 行有 1 1 1 个数,表示第 n − 1 n-1 n−1 个地窖至第 n n n 个地窖有否路径连接。(为 0 0 0 表示没有路径,为 1 1 1 表示有路径)。
输出格式
第一行表示挖得最多地雷时的挖地雷的顺序,各地窖序号间以一个空格分隔,不得有多余的空格。
第二行只有一个数,表示能挖到的最多地雷数。
样例 #1
样例输入 #1
5
10 8 4 7 6
1 1 1 0
0 0 0
1 1
1
样例输出 #1
1 3 4 5
27
提示
【题目来源】
NOIP 1996 提高组第三题
分析
DFS
,对每个顶点跑一遍dfs
,记录从这个点开始的地雷数,res
取最大,dp[i]
表示到达这个点的最多地雷数,由于是顺着来的,因此需要属于pre
记录前一个顶点的位置,方便输出路径
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 25;
int a[N];
int dp[N];
bool mp[N][N];
bool vis[N];
bool st[N];
int pre[N];
int res = 0;
int n;
int pos = 0;
void dfs(int x) {
if(res < dp[x]) {
pos = x;
res = dp[x];
}
for(int i = 1; i <= n; i ++ ) {
if(mp[x][i] && !vis[i]) {
vis[i] = true;
if(dp[i] <= dp[x] + a[i]) {
pre[i] = x;
dp[i] = dp[x] + a[i];
}
vis[i] = false;
}
}
}
void print(int x) {
vector<int> res;
while(x) {
res.push_back(x);
x = pre[x];
}
for(int i = res.size() - 1; i >= 0; i -- ) {
cout << res[i] << " ";
}
cout << endl;
}
int main() {
cin >> n;
for(int i = 1; i <= n; i ++ ) cin >> a[i];
for(int i = 1; i <= n - 1; i ++ ) {
for(int j = 1; j <= n - i; j ++ ) {
int a;
cin >> a;
if(a == 1) mp[i][i + j] = 1;
}
}
// print();
for(int i = 1; i <= n; i ++ ) {
dp[i] = max(dp[i], a[i]);
vis[i] = true;
dfs(i);
vis[i] = false;
}
print(pos);
cout << res << endl;
return 0;
}
[SHOI2002] 滑雪
题目描述
Michael 喜欢滑雪。这并不奇怪,因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael 想知道在一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子:
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度会减小。在上面的例子中,一条可行的滑坡为 24 − 17 − 16 − 1 24-17-16-1 24−17−16−1(从 24 24 24 开始,在 1 1 1 结束)。当然 25 25 25- 24 24 24- 23 23 23- … \ldots …- 3 3 3- 2 2 2- 1 1 1 更长。事实上,这是最长的一条。
输入格式
输入的第一行为表示区域的二维数组的行数 R R R 和列数 C C C。下面是 R R R 行,每行有 C C C 个数,代表高度(两个数字之间用 1 1 1 个空格间隔)。
输出格式
输出区域中最长滑坡的长度。
样例 #1
样例输入 #1
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
样例输出 #1
25
提示
对于 100 % 100\% 100% 的数据, 1 ≤ R , C ≤ 100 1\leq R,C\leq 100 1≤R,C≤100。
分析
记忆化DFS
,由于起点不确定,因此需要遍历每个点,对每个点做一遍dfs
,然后res
取最大,不记忆化会T
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int dp[N][N];
int n, m;
int a[N][N];
int dx[4] = {0, 1, 0, -1};
int dy[4] = {-1, 0, 1, 0};
int res = 1;
bool vis[N][N];
int dfs(int x, int y) {
if(dp[x][y]) return dp[x][y];
dp[x][y] = 1;
for(int i = 0; i < 4; i ++ ) {
int nx = x + dx[i], ny = y + dy[i];
if(nx <= 0 || ny <= 0 || nx > n || ny > m) continue;
if(vis[nx][ny]) continue;
vis[nx][ny] = true;
if(a[nx][ny] < a[x][y]) dp[x][y] = max(dfs(nx, ny) + 1, dp[x][y]);
vis[nx][ny] = false;
}
return dp[x][y];
}
int main() {
cin >> n >> m;
for(int i = 1; i <= n; i ++ ) {
for(int j = 1; j <= m; j ++ ) {
cin >> a[i][j];
}
}
for(int i = 1; i <= n; i ++ ) {
for(int j = 1; j <= m; j ++ ) {
res = max(res, dfs(i, j));
}
}
cout << res << endl;
return 0;
}