UVA10285 最长的滑雪路径 Longest Run on a Snowboard
题目描述
ben 喜欢滑雪。(以下简叙)在一个 R×C(R,C≤100)的整数矩阵上找一条高度严格递减的最长路。起点任意,但每次只能沿着上下左右 4 个方向之一走一格,并且不能走出矩阵外。如图所示,最长路是按照高度 25,24,23,...,2,1 这样走,长度为 25。矩阵中的数均为0∼100。
输入格式
有多组数据。
第一行为一个整数 N,表示数据组数。 对于每组数据,第一行包括一个字符串和两个整数 R,C,为此滑雪者的姓名和矩阵的长宽。
输出格式
对于每组数据,输出一行,格式为:name: answer
name 为当前数据的滑雪者姓名,answer 为你的答案。
解:
深度优先搜索 :从任意点 (x, y) 出发,向四个方向中高度更低的点递归搜索。
备忘录 :用一个 dp[x][y] 数组记录从点 (x, y) 出发的最长路径长度。如果某个点已经计算过,就直接返回结果,避免重复计算。
公式 :dp[x][y] = 1 + max(dfs(nx, ny)) (对所有高度低于 (x, y) 的相邻点 (nx, ny) 取最大值)。
cpp
#include<bits/stdc++.h>
using namespace std;
int r, c;
int hight[105][105];//存储每个点的高度
int dp[105][105];//dp[i][j]表示从(i,j)出发的最长路径长度
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
int dfs(int x,int y){
if (dp[x][y] != -1) return dp[x][y];
int max_len = 1;
for(int i = 0;i < 4;i++){
int nx = x + dx[i];
int ny = y + dy[i];
if(nx >= 1 && nx <= r && ny >= 1 && ny <= c){//保证在边界内
if(hight[x][y] > hight[nx][ny]){
max_len = max(max_len, dfs(nx, ny) + 1);
}
}
}
return dp[x][y] = max_len;
}
int main(){
int n;
cin >> n;
while(n--){
string name;
cin >> name >> r >> c;
for(int i = 1;i <= r;i++){
for(int j = 1;j <= c;j++){
cin >> hight[i][j];
}
}
memset(dp,-1,sizeof(dp));
int ans = 0;
// 从每个点出发尝试
for (int i = 1; i <= r; i++) {
for (int j = 1; j <= c; j++) {
ans = max(ans, dfs(i, j));
}
}
cout << name << ": " << ans << endl;
}
}
UVA10118 免费糖果 Free Candies
题意翻译
桌上有4堆糖果,每堆有N(N≤40)颗。佳佳有一个最多可以装5颗糖的小篮子。他每次 选择一堆糖果,把最顶上的一颗拿到篮子里。如果篮子里有两颗颜色相同的糖果,佳佳就把 它们从篮子里拿出来放到自己的口袋里。如果篮子满了而里面又没有相同颜色的糖果,游戏 结束,口袋里的糖果就归他了。当然,如果佳佳足够聪明,他有可能把堆里的所有糖果都拿 走。为了拿到尽量多的糖果,佳佳该怎么做呢?
来自:刘汝佳《算法竞赛入门经典》
解:
-
位掩码表示篮子:用整数的 20 个位表示每种颜色是否在篮子中
-
四维 DP :
dp[a][b][c][d]表示每堆取到第几个位置时的状态 -
不需要 list/vector:用位运算判断是否存在相同颜色
cpp
#include<bits/stdc++.h>
using namespace std;
int pile[45][5]; // 存储每堆的糖果,pile[i][j] 表示第j堆的第i个
int top[5]; // 每堆的当前顶部位置
int n;
int dp[45][45][45][45]; // DP数组,记录每个状态的最大对数
int dfs(int basket, int pairs) {
// basket 是一个位掩码,表示篮子中哪些颜色的糖果存在
// 因为颜色范围 1-20,可以用 20 位表示
int& res = dp[top[1]][top[2]][top[3]][top[4]];
if(res != -1) return res;
res = pairs; // 记录当前对数
// 尝试从每堆取糖果
for(int i = 1; i <= 4; i++) {
if(top[i] > n) continue; // 这堆已经取完
int candy = pile[top[i]][i];
int bit = 1 << candy; // 糖果对应的位
if(basket & bit) {
// 篮子中有相同颜色:取出这一对
top[i]++;
res = max(res, dfs(basket & ~bit, pairs + 1));
top[i]--;
} else {
// 篮子中没有相同颜色
if(__builtin_popcount(basket) < 5) { // 篮子未满
top[i]++;
res = max(res, dfs(basket | bit, pairs));
top[i]--;
}
}
}
return res;
}
int main() {
// freopen("in.txt","r",stdin);
while(cin >> n && n) {
// 输入:第 i 行表示每堆的第 i 个糖果(从顶部到底部)
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= 4; j++) {
cin >> pile[i][j];
}
}
// 初始化
memset(dp, -1, sizeof(dp));
for(int i = 1; i <= 4; i++) top[i] = 1;
// 开始搜索
int ans = dfs(0, 0);
cout << ans << endl;
}
return 0;
}