洛谷题单指南-动态规划1-P2196 [NOIP1996 提高组] 挖地雷

原题链接:https://www.luogu.com.cn/problem/P2196

题意解读:求一条路径,使得所有点地雷数之和最大。

解题思路:

1、DFS

先建图,再从1~n点分别进行一次DFS,记录过程中地雷总数最大的,并且同时记录遍历的顺序。数据量不大,直接就可以过。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 25;
int n, x, a[N];
vector<int> g[N];
bool flag[N];
int ans;
vector<int> order, ansorder;

void dfs(int u, int sum)
{
    if(sum > ans)
    {
        ans = sum; //更新答案
        ansorder = order; //更新最大答案下的顺序
    }
    flag[u] = true;
    for(int i = 0; i < g[u].size(); i++)
    {
        int v = g[u][i];
        if(!flag[v])
        {
            flag[v] = true; //标记
            order.push_back(v); //遍历顺序中加入v
            dfs(v, sum + a[v]);
            order.pop_back(); //回溯,遍历顺序中弹出v
            flag[v] = false; //回溯
        }
    }
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++)
    {
        for(int j = i + 1; j <= n; j++)
        {
            cin >> x;
            if(x == 1) g[i].push_back(j); //领接表建图
        }
    }
    for(int i = 1; i <= n; i++) //从每个点开始dfs
    {
        memset(flag, 0, sizeof(flag)); //重置flag
        order.clear(); //清空保存的遍历顺序
        order.push_back(i); //从i开始
        dfs(i, a[i]);
    }
    for(int v : ansorder) cout << v << " ";
    cout << endl;
    cout << ans;
    return 0;
}

2、DP

比较类似于最长上升子序列

设dp[i]表示以i结束的点的路径采集的最大地雷数

则dp[i] = max(dp[i], dp[j] + a[i]),j是所有到i有路径的点,用g[][]表示图的领接矩阵就是g[j][i] == 1

注意,所有的路径,都是编号小的到编号大的,没有点到1号点有路径

初始化时dp[1] = a[1], a[]保存所有点的地雷数

至于,要输出路径中所有的点,只需要在比较时,把每个点是从哪个点走过来的记录下来,再递归输出,或者借助堆栈输出即可。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 25;
int n;
int a[N]; //每个点的地雷数
int g[N][N]; //领接矩阵
int dp[N]; //dp[i]表示以i结尾的路径中挖到地雷的最大值
int from[N]; //记录每个点是从哪个点走过来的
int ans, last;

void print(int last)
{
    if(!last) return;
    print(from[last]);
    cout << last << " ";
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++)
        for(int j = i + 1; j <= n; j++)
            cin >> g[i][j]; //g[i][j]=1表示i->j有边

    dp[1] = a[1];
    for(int i = 2; i <= n; i++)
    {
        dp[i] = a[i];
        for(int j = 1; j < i; j++) //到i点有路径的都是比i小的
        {
            if(g[j][i]) 
            {
                if(dp[j] + a[i] > dp[i])
                {
                    dp[i] = dp[j] + a[i];
                    from[i] = j; //记录i是从j走过来的
                }
            }
        }
    }
    for(int i = 1; i <= n; i++)
    {
        if(dp[i] > ans)
        {
            ans = dp[i];
            last = i; //终点
        }
    }

    //print(last); //递归输出
    
    //堆栈输出
    stack<int> order;
    order.push(last);
    while(from[last])
    {
        last = from[last];
        order.push(last);
    }
    while(order.size())
    {
        cout << order.top() << " ";
        order.pop();
    }
    
    cout << endl;
    cout << ans;
    return 0;
}

 

posted @ 2024-04-18 16:06  五月江城  阅读(68)  评论(0编辑  收藏  举报