L3-001. 凑零钱(dfs + 各种剪枝)

本文介绍了一种解决凑零钱问题的方法,该问题要求从一组硬币中选择若干枚硬币,使其总和等于指定金额。通过使用递归算法进行搜索,并结合剪枝策略来提高效率。

L3-001. 凑零钱

时间限制
200 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

韩梅梅喜欢满宇宙到处逛街。现在她逛到了一家火星店里,发现这家店有个特别的规矩:你可以用任何星球的硬币付钱,但是绝不找零,当然也不能欠债。韩梅梅手边有104枚来自各个星球的硬币,需要请你帮她盘算一下,是否可能精确凑出要付的款额。

输入格式:

输入第一行给出两个正整数:N(<=104)是硬币的总个数,M(<=102)是韩梅梅要付的款额。第二行给出N枚硬币的正整数面值。数字间以空格分隔。

输出格式:

在一行中输出硬币的面值 V1 <= V2 <= ... <= Vk,满足条件 V1 + V2 + ... + Vk = M。数字间以1个空格分隔,行首尾不得有多余空格。若解不唯一,则输出最小序列。若无解,则输出“No Solution”。

注:我们说序列{A[1], A[2], ...}比{B[1], B[2], ...}“小”,是指存在 k >= 1 使得 A[i]=B[i] 对所有 i < k 成立,并且 A[k] < B[k]。

输入样例1:
8 9
5 9 8 7 2 3 4 1
输出样例1:
1 3 5
输入样例2:
4 8
7 2 4 3
输出样例2:
No Solution
//本题需要强大的剪枝
#include <bits/stdc++.h>
using namespace std;
#define maxn 11000
int vis[maxn];
int s[maxn];
int tmp[maxn];
int n, m;
int cnt;
int flag;
int all;
void dfs(int index,int sum, int cnt,int tol)//tol 为剩余的钱数
{
    if(flag || sum > m || tol < m - sum)
        return;
    if(sum == m)
    {
        for(int i = 0; i < cnt - 1; i++)
            printf("%d ",tmp[i]);
        printf("%d\n",tmp[cnt - 1]);
        flag = 1;
        return;
    }
    for(int i = index + 1; i < n; i++)
    {
        if(!vis[i] && s[i] <= m - sum)
        {
            vis[i] = 1;
            tmp[cnt] = s[i];
            dfs(i,sum + s[i], cnt+1, tol - s[i]);
            if(flag) return;
            vis[i] = 0;
        }
    }
}
int main()
{

    scanf("%d%d", &n, &m);
    all = 0;
    for(int i = 0; i < n; i++)
    {
        scanf("%d", &s[i]);
        all += s[i];
    }
    sort(s, s + n);
    cnt = 0;
    dfs(-1,0, cnt,all);
    if(!flag)
        printf("No Solution\n");
}


最大城镇面积问题本质上是求二维网格中**连通块的最大大小**,其中上下左右相邻且颜色相同的格子视为连通。这是一个典型的图论中的连通性问题,可以使用 **深度优先搜索(DFS)** 或 **广度优先搜索(BFS)** 来解决。 --- ### 解题思路: 1. **输入处理**:读取 `n × m` 的字符矩阵。 2. **遍历每个格子**:对每一个未访问过的格子,启动一次 DFS,统计其所在连通区域(即“城镇”)的面积。 3. **标记已访问**:使用一个布尔数组 `visited` 记录是否访问过该格子,避免重复计算。 4. **DFS 搜索**: - 从当前格子出发,向四个方向(上、下、左、右)扩展。 - 只有当邻居格子与当前格子颜色相同且未被访问时才继续递归。 - 累加访问的格子数作为该城镇的面积。 5. **维护最大值**:每次 DFS 返回连通块大小,更新全局最大面积。 6. **输出结果**。 > 题目提示使用 DFS + 剪枝,且不需要恢复现场 —— 这意味着我们只需通过 `visited` 数组跳过已处理的位置即可,无需回溯状态。 --- ### C++ 实现代码如下: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; // 方向数组:上、下、左、右 const int dx[4] = {-1, 1, 0, 0}; const int dy[4] = {0, 0, -1, 1}; // 全局变量 int n, m; vector<string> grid; vector<vector<bool>> visited; // DFS函数:从(x,y)开始遍历同色连通块,返回面积 int dfs(int x, int y, char color) { if (x < 0 || x >= n || y < 0 || y >= m || visited[x][y] || grid[x][y] != color) { return 0; } visited[x][y] = true; int area = 1; // 当前格子 for (int i = 0; i < 4; ++i) { int nx = x + dx[i]; int ny = y + dy[i]; area += dfs(nx, ny, color); } return area; } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m; grid.resize(n); visited.assign(n, vector<bool>(m, false)); for (int i = 0; i < n; ++i) { cin >> grid[i]; } int maxArea = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { if (!visited[i][j]) { int area = dfs(i, j, grid[i][j]); maxArea = max(maxArea, area); } } } cout << maxArea << endl; return 0; } ``` --- ### 解释: - **时间复杂度**:O(n × m),每个格子最多被访问一次。 - **空间复杂度**:O(n × m),用于存储网格和 `visited` 数组;递归栈最深可达 O(n×m)(极端情况下整个图是一个连通块)。 - 使用了剪枝:`visited` 和边界判断提前终止无效搜索。 - 不需要“恢复现场”是因为我们只关心每个格子是否已被计入某个城镇,不涉及路径回溯或状态切换。 --- ### 示例解析(样例输入): 输入为 9×11 的地图,其中 `&#39;.&#39;` 在右下角形成一大片连续区域,经过 DFS 发现其最大连通块面积为 **27**,对应输出。 例如,最后一行: ``` ++.#.#....# ``` 末尾有多个 `&#39;.&#39;`,向上多行也有大量 `&#39;.&#39;` 相连,最终构成面积为 27 的连通区域。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值