这是一道变形题,不得不说是好题啊
题目大意是,有一张n*m的地图,每个点上可能是数字,代表矿石的数目,可能是*,表示一个传送阵,送往某个坐标,可能是#,代表不通。每次矿车只能往右方或者下方走一格,那么这就可以转化为一个有向图了。
每个点,往其右方和下方相邻的点建有向边,如果是#,就不建边了,如果是*,就要把*的位置跟其传送的位置建一条边。
之后就要求强连通分量,然后缩点了,再之后就是DP求最优解了,跟3160是一致的,按照拓扑序逆序DP,最后输出的是起点所在的新点的DP值
2012年7月1日:重写了此题,用topsort无数个wa,只好用spfa最长路水过,然后终于用topsort水过
依然是用Kosaraju
/* ID: sdj22251 PROG: subset LANG: C++ */ #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> #define LOCA #define MAXN 5005 #define INF 100000000 #define eps 1e-7 using namespace std; struct Edge { int v, next; }edge[10 * MAXN], revedge[10 * MAXN], newedge[MAXN]; int head[MAXN], revhead[MAXN], e, visited[MAXN], newhead[MAXN]; int order[MAXN], cnt, id[MAXN], val[MAXN], newval[MAXN]; int uu[5 * MAXN], vv[5 * MAXN], out[MAXN], dp[MAXN], num; int n, m; int ans; char mp[50][50]; struct Point { int x, y; }p[MAXN]; void init() { e = 0; cnt = 0; num = 0; memset(head, -1, sizeof(head)); memset(revhead, -1, sizeof(revhead)); memset(newhead, -1, sizeof(newhead)); memset(out, 0 , sizeof(out)); memset(newval, 0, sizeof(newval)); memset(dp, 0, sizeof(dp)); memset(val, 0, sizeof(val)); } void insert(const int &x, const int &y) { edge[e].v = y; edge[e].next = head[x]; head[x] = e; revedge[e].v = x; revedge[e].next = revhead[y]; revhead[y] = e; e++; } void newinsert(const int &x, const int &y) { newedge[e].v = y; newedge[e].next = newhead[x]; newhead[x] = e++; } void readdata() { for(int i = 0; i < n; i++) scanf("%s", mp[i]); for(int i = 0; i < n; i++) { for(int j = 0; j < m; j++) { if(mp[i][j] == '#') continue; if(mp[i][j] == '*') { p[cnt].x = i; p[cnt++].y = j; } else { val[i * m + j + 1] = mp[i][j] - '0'; } if(j < m - 1) { if(mp[i][j + 1] != '#') { uu[num] = i * m + j + 1; vv[num] = i * m + j + 2; insert(uu[num], vv[num]); num++; } } if(i < n - 1) { if(mp[i + 1][j] != '#') { uu[num] = i * m + j + 1; vv[num] = (i + 1) * m + j + 1; insert(uu[num], vv[num]); num++; } } } } for(int i = 0; i < cnt; i++) { int x, y; scanf("%d%d", &x, &y); int xx = p[i].x; int yy = p[i].y; uu[num] = xx * m + yy + 1; vv[num] = x * m + y + 1; if(mp[x][y] != '#') { insert(uu[num], vv[num]); num++; } } } void dfs(int u) { visited[u] = 1; for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v; if(!visited[v]) dfs(v); } order[cnt++] = u; } void dfs_rev(int u) { visited[u] = 1; id[u] = cnt; if(val[u] > 0) newval[cnt] += val[u]; for(int i = revhead[u]; i != -1; i = revedge[i].next) { int v = revedge[i].v; if(!visited[v]) dfs_rev(v); } } void Kosaraju() { init(); readdata(); memset(visited, 0, sizeof(visited)); cnt = 0; for(int i = 1; i <= n * m; i++) { if(!visited[i]) dfs(i); } memset(visited, 0, sizeof(visited)); cnt = 0; for(int i = n * m - 1; i >= 0; i--) { if(!visited[order[i]]) { cnt++; dfs_rev(order[i]); } } e = 0; for(int i = 0; i < num; i++) { int u = id[uu[i]]; int v = id[vv[i]]; if(u != v) newinsert(u, v); } ans = 0; for(int u = cnt; u >= 1; u--) { int tmp = 0; dp[u] = newval[u]; for(int i = newhead[u]; i != -1; i = newedge[i].next) { int v = newedge[i].v; tmp = max(tmp, dp[v]); } dp[u] += tmp; } ans = dp[id[1]]; } int main() { int T; scanf("%d", &T); while(T--) { scanf("%d%d", &n, &m); Kosaraju(); printf("%d\n", ans); } return 0; }
另外附上tarjan的
偷懒用了vector
#include <iostream> #include <cstdio> #include <cstring> #include <vector> #define MAXN 1666 #define MAXM 55555 #define INF 1000000000 using namespace std; char mp[55][55]; int dfn[MAXN], low[MAXN], index, fa[MAXN], instack[MAXN]; int scc, n, m, cnt; int s[MAXN], top, q[MAXM]; int val[MAXN], newval[MAXN], dp[MAXN], vis[MAXN]; int magic[MAXN]; struct Edge { int v, w; Edge(){} Edge(int a, int b){v = a; w = b;} }; vector<int>g[MAXN]; vector<Edge>dag[MAXN]; void init() { index = top = scc = cnt = 0; memset(dfn, 0, sizeof(dfn)); memset(instack, 0, sizeof(instack)); memset(newval, 0, sizeof(newval)); memset(val, 0, sizeof(val)); for(int i = 0; i < MAXN; i++) g[i].clear(), dag[i].clear(); } void tarjan(int u) { dfn[u] = low[u] = ++index; s[++top] = u; instack[u] = 1; int size = g[u].size(); int v; for(int i = 0; i < size; i++) { v = g[u][i]; if(!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if(instack[v]) low[u] = min(low[u], dfn[v]); } if(dfn[u] == low[u]) { scc++; do { v = s[top--]; instack[v] = 0; fa[v] = scc; newval[scc] += val[v]; }while(v != u); } } void build() { for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) { int u = i * m + j; if(mp[i][j] == '#') {val[u] = -1; continue;} else if(mp[i][j] == '*') magic[cnt++] = u; else val[u] = mp[i][j] - '0'; if(j < m - 1 && mp[i][j + 1] != '#') g[u].push_back(u + 1); if(i < n - 1 && mp[i + 1][j] != '#') g[u].push_back(u + m); } int x, y; for(int i = 0; i < cnt; i++) { scanf("%d%d", &x, &y); int u = x * m + y; if(val[u] == -1) continue; g[magic[i]].push_back(u); } } void spfa(int src) { for(int i = 0; i <= scc; i++) dp[i] = -INF; memset(vis, 0, sizeof(vis)); vis[src] = 1; int h = 0, t = 0; q[t++] = src; dp[src] = 0; while(h < t) { int u = q[h++]; vis[u] = 0; int size = dag[u].size(); for(int i = 0; i < size; i++) { int v = dag[u][i].v; int w = dag[u][i].w; if(dp[v] < dp[u] + w) { dp[v] = dp[u] + w; if(!vis[v]) { vis[v] = 1; q[t++] = v; } } } } } void solve() { for(int i = 0; i < n * m; i++) if(!dfn[i]) tarjan(i); for(int u = 0; u < n * m; u++) { int size = g[u].size(); for(int j = 0; j < size; j++) { int v = g[u][j]; if(fa[u] != fa[v]) dag[fa[u]].push_back(Edge(fa[v], newval[fa[v]])); } } dag[0].push_back(Edge(fa[0], newval[fa[0]])); spfa(0); int ans = 0; for(int i = 0; i <= scc; i++) ans = max(ans, dp[i]); printf("%d\n", ans); } int main() { int T; scanf("%d", &T); while(T--) { scanf("%d%d", &n, &m); init(); for(int i = 0; i < n; i++) scanf("%s", mp[i]); build(); solve(); } return 0; }
拓扑排序版本 之前一直错是因为在拓扑排序开始时,只把0结点属于的强连通分量加入了队列, 其实不该这样。要把所有入度为0的结点加入,然后正常的拓扑排序,得到序列后,逆序进行DP就可以得到结果
#include <iostream> #include <cstdio> #include <cstring> #include <vector> #define MAXN 1666 #define MAXM 55555 #define INF 1000000000 using namespace std; char mp[55][55]; int dfn[MAXN], low[MAXN], index, fa[MAXN], instack[MAXN]; int scc, n, m, cnt; int s[MAXN], top, q[MAXM]; int val[MAXN], newval[MAXN], dp[MAXN], vis[MAXN]; int magic[MAXN], in[MAXN]; vector<int>g[MAXN]; vector<int>dag[MAXN]; void init() { index = top = scc = cnt = 0; memset(dfn, 0, sizeof(dfn)); memset(instack, 0, sizeof(instack)); memset(newval, 0, sizeof(newval)); memset(val, 0, sizeof(val)); memset(in, 0, sizeof(in)); for(int i = 0; i < MAXN; i++) g[i].clear(), dag[i].clear(); } void tarjan(int u) { dfn[u] = low[u] = ++index; s[++top] = u; instack[u] = 1; int size = g[u].size(); int v; for(int i = 0; i < size; i++) { v = g[u][i]; if(!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if(instack[v]) low[u] = min(low[u], dfn[v]); } if(dfn[u] == low[u]) { scc++; do { v = s[top--]; instack[v] = 0; fa[v] = scc; newval[scc] += val[v]; }while(v != u); } } void build() { for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) { int u = i * m + j; if(mp[i][j] == '#') {val[u] = -1; continue;} else if(mp[i][j] == '*') magic[cnt++] = u; else val[u] = mp[i][j] - '0'; if(j < m - 1 && mp[i][j + 1] != '#') g[u].push_back(u + 1); if(i < n - 1 && mp[i + 1][j] != '#') g[u].push_back(u + m); } int x, y; for(int i = 0; i < cnt; i++) { scanf("%d%d", &x, &y); int u = x * m + y; if(val[u] == -1) continue; g[magic[i]].push_back(u); } } void topsort() { int h = 0, t = 0; for(int i = 1; i <= scc; i++) if(in[i] == 0) q[t++] = i; while(h < t) { int u = q[h++]; int size = dag[u].size(); for(int i = 0; i < size; i++) { int v = dag[u][i]; in[v]--; if(in[v] == 0) q[t++] = v; } } memset(dp, 0, sizeof(dp)); for(int i = t - 1; i >= 0; i--) { int u = q[i]; int tmp = 0; dp[u] = newval[u]; int size = dag[u].size(); for(int j = 0; j < size; j++) { int v = dag[u][j]; tmp = max(tmp, dp[v]); } dp[u] += tmp; } printf("%d\n", dp[fa[0]]); } void solve() { for(int i = 0; i < n * m; i++) if(!dfn[i]) tarjan(i); for(int u = 0; u < n * m; u++) { int size = g[u].size(); for(int j = 0; j < size; j++) { int v = g[u][j]; if(fa[u] != fa[v]) {dag[fa[u]].push_back(fa[v]); in[fa[v]]++;} } } topsort(); } int main() { int T; scanf("%d", &T); while(T--) { scanf("%d%d", &n, &m); init(); for(int i = 0; i < n; i++) scanf("%s", mp[i]); build(); solve(); } return 0; }