[SMOJ1831]小岛II
来源:互联网 发布:凯聪网络摄像机软件 编辑:程序博客网 时间:2024/04/29 02:27
看到这题,我首先想到是参考之前所做的“最优贸易”一题,但是事实上两题的做法是大相径庭的。
一点一点来分解题目,我们就能够有一个比较清晰的思路。
首先,价值可能为负数,而经过结点是可以取也可以不取,那么显然不应该取负数。
从任意结点出发,在任意结点停止。也就是说,对于一个环,我们爱怎么走就怎么走。显然最优方案,应该把一个强连通分量内权值为正数的点都要取一遍。
跟之前题目的策略类似,对于有向图上的最优值问题,可以考虑一下:是否可以将图缩点,然后利用拓扑图的性质,做一些 DP,或是搜索?
因为是从任意点开始,任意点结束。在一个有向图中,相对来说,考虑当前结点的前驱结点能够到达当前结点,从而用当前结点的值去改进或更新前驱结点的值,是一种比较好的策略。同时,我们知道,如果固定从某个点开始,那么就可以看作一个确定的子问题,当它的值求得之后,无论是从哪个前驱结点到达当前结点,之后能取得的最优值都是确定的。
综上所述,就可以枚举起点,之后用记忆化搜索的方式,计算
如果拓展一下,想一想是否可以用 spfa 搞呢?
本题是不太适合的。因为 spfa 的松弛里面可能出现多次取同一结点价值的情况,如果再加个状态记录显然是不现实的。所以要分析题目,各种角度考虑做法,切忌思维定式。同时还是那句话,积累解决同一类题目的常见策略。
参考代码:
#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <stack>#include <queue>using namespace std;const int maxn = 1e5 + 100;struct Edge { int to, next; } edge[maxn], edge1[maxn];int cntEdge, cntEdge1;int a[maxn], b[maxn];int n, m;int value[maxn], belong[maxn];int head[maxn], head1[maxn];void addEdge(int u, int v) { edge[++cntEdge].to = v; edge[cntEdge].next = head[u]; head[u] = cntEdge;}void addEdge1(int u, int v) { edge1[++cntEdge1].to = v; edge1[cntEdge1].next = head1[u]; head1[u] = cntEdge1;}int timeStamp;int dfn[maxn], low[maxn];int cntScc;bool instack[maxn];stack <int> st;int val[maxn];void tarjan(int root) { dfn[root] = low[root] = ++timeStamp; instack[root] = true; st.push(root); for (int i = head[root]; i; i = edge[i].next) { int To = edge[i].to; if (!dfn[To]) { tarjan(To); low[root] = min(low[root], low[To]); } else if (instack[To]) low[root] = min(low[root], dfn[To]); } if (dfn[root] == low[root]) { ++cntScc; int cur; do { cur = st.top(); st.pop(); instack[cur] = false; belong[cur] = cntScc; } while (cur != root); }}int dp[maxn];int ans;bool vis[maxn]; //记忆化,搜索过的标记void dfs(int r) { if (vis[r]) return; else vis[r] = true; for (int i = head1[r]; i; i = edge1[i].next) { dfs(edge1[i].to); dp[r] = max(dp[r], dp[edge1[i].to]); } dp[r] += val[r];}int main(void) { freopen("1831.in", "r", stdin); freopen("1831.out", "w", stdout); int r; scanf("%d", &r); while (r--) { scanf("%d%d", &n, &m); for (int i = 0; i < n; i++) scanf("%d", &value[i]); memset(head, 0, sizeof head); cntEdge = 0; for (int i = 0; i < m; i++) { scanf("%d%d", &a[i], &b[i]); addEdge(a[i], b[i]); } timeStamp = cntScc = 0; memset(dfn, 0, sizeof dfn); memset(val, 0, sizeof val); while (!st.empty()) st.pop(); memset(belong, 0, sizeof belong); for (int i = 0; i < n; i++) if (!dfn[i]) tarjan(i); memset(head1, 0, sizeof head1); cntEdge1 = 0; for (int i = 0; i < n; i++) { //缩点后重新构图 for (int j = head[i]; j; j = edge[j].next) if (belong[i] != belong[edge[j].to]) addEdge1(belong[i], belong[edge[j].to]); if (value[i] > 0) val[belong[i]] += value[i]; //正数才取 } memset(dp, 0, sizeof dp); memset(vis, false, sizeof vis); ans = 0; for (int i = 1; i <= cntScc; i++) { //考虑从每一个强连通分量出发 dfs(i); ans = max(ans, dp[i]); } printf("%d\n", ans); } return 0;}
0 0
- [SMOJ1831]小岛II
- LeetCode 305. Number of Islands II(小岛)
- 小岛梦
- 死亡小岛
- [SMOJ1830]小岛
- 小岛面积
- 连接小岛
- 小岛问题
- 【字符串】转自小岛
- 小岛和雷达
- 舟山群岛小岛攻略
- 小岛场景搭建
- 二维数组寻找小岛
- AHOI 2015 小岛
- 待字闺中之死亡小岛分析
- 贪心 51Nod1460 连接小岛
- 51Nod-1460-连接小岛
- 51nod-1460 连接小岛
- TabLayout+ViewPager+Fragment懒加载实现
- 关于dubbo服务的xml配置文件报错的问题
- Android Studio中如何引用.aar包
- 将sqlplus查询结果输出为文件
- Android AndroidStudio查看Key的SHA1 和MD5
- [SMOJ1831]小岛II
- Java进阶(十一)部分数据类型取值范围
- 387. First Unique Character in a String的C++解法
- (九)RabbitMQ消息队列-通过Headers模式分发消息
- CSS3有哪些新特性?
- 403 Access Denied
- Redis安装+spring注解集成Redis
- “快的那个,会被慢的拖死”,记不断奔跑、看着阿里云产品管控慢慢长大的那个人——尹书威
- 题目1464:Hello World for U