http://poj.org/problem?id=3592
/*
题意:给定一个n*m的矩阵,你从左上角出发,规定只能往当前点的右边或者下边走,其中还有一些特殊点*具有特殊的力量可以把你传到特定的一个点(你可以选择传送也可以选择不传送),问从左上角出发到不能走下去,最多能获得的矿石量(每个方格对应着一个数字表示矿石数量)。点#直接跳过
思路:首先build1根据题意描述,见图,将二位矩阵转化为一维的点建图,每个点可以向右向下建立有向边,点*还可以向传送点建边。建完后tarjan缩点,然后在根据缩点后的点建图,添加超级源点s,权值为i-j sum[j], 求最长路即可的结果;
中间数组开成了44贡献了几次wa,转化为1维后是40*40了。。。
*/
#include <cstdio> #include <cstring> #include <iostream> #include <queue> #define maxn 44 #define N 2002 using namespace std; int dir[2][2] = {{0,1},{1,0}}; const int inf = 99999999; struct node { int v,w; int next; }g1[N*N],g2[N*N]; int head1[N],head2[N],ct1,ct2; int low[N],dfn[N],stack[N],val[N]; int bcnt,top,index,num[maxn][maxn],kpos[N]; int belong[N],sum[N],dis[N]; bool inq[N],ins[N]; int n,m,ct,knum,s; char str[maxn][maxn]; void add1(int u,int v) { g1[ct1].v = v; g1[ct1].next = head1[u]; head1[u] = ct1++; } void add2(int u,int v,int w) { g2[ct2].v = v; g2[ct2].w =w; g2[ct2].next = head2[u]; head2[u] = ct2++; } void tarjan(int i) { int k,j; low[i] = dfn[i] = ++index; ins[i] = true; stack[++top] = i; for (k = head1[i]; k != -1; k = g1[k].next) { int j = g1[k].v; if (!dfn[j]) { tarjan(j); low[i] = min(low[i],low[j]); } else if (ins[j]) { low[i] = min(low[i],dfn[j]); } } if (dfn[i] == low[i]) { bcnt++; do { j = stack[top--]; ins[j] = false; belong[j] = bcnt; }while (j != i); } } void build1() { int i,j,k,x,y; memset(num,0,sizeof(num)); memset(kpos,0,sizeof(kpos)); memset(val,0,sizeof(val)); ct = knum = 0; scanf("%d%d",&n,&m); for (i = 0; i < n; ++i) { scanf("%s",str[i]); for (j = 0; j < m; ++j) { if (str[i][j] == '#') continue; num[i][j] = ++ct; if (str[i][j] >= '0' && str[i][j] <= '9') val[num[i][j]] = str[i][j] - '0'; else { kpos[knum++] = num[i][j];//记录每个*点,应为后边依次输入其传输的坐标 val[num[i][j]] = 0; } } } memset(head1,-1,sizeof(head1)); ct1 = 0; for (i = 0; i < n; ++i) { for (j = 0; j < m; ++j) { if (str[i][j] == '#') continue; for (k = 0; k < 2; ++k) { int tx = i + dir[k][0]; int ty = j + dir[k][1]; if (tx >= 0 && tx < n && ty >= 0 && ty < m) add1(num[i][j],num[tx][ty]); } } } //根据*点传输的坐标添加边 for (i = 0; i < knum; ++i) { scanf("%d%d",&x,&y); add1(kpos[i],num[x][y]); } memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(ins,false,sizeof(ins)); memset(belong,0,sizeof(belong)); top = index = bcnt = 0; for (i = 1; i <= ct; ++i) { if (!dfn[i]) tarjan(i);//tarjan缩点 } //缩点后计算权值 memset(sum,0,sizeof(sum)); for (i = 1; i <= ct; ++i) sum[belong[i]] += val[i]; } void build2() { int i,k; s = 0; memset(head2,-1,sizeof(head2));//加入超级源点 ct2 = 0; add2(s,belong[1],sum[belong[1]]); for (i = 1; i <= ct; ++i) { for (k = head1[i]; k != -1; k = g1[k].next) { int j = g1[k].v; if (belong[i] != belong[j]) { add2(belong[i],belong[j],sum[belong[j]]); } } } } void spfa(int s) { int i; queue<int>q; for (i = 0; i < N; ++i) { dis[i] = -inf; inq[i] = false; } q.push(s); dis[s] = 0; inq[s] = true; while (!q.empty()) { int u = q.front(); q.pop(); inq[u] = false; for (i = head2[u]; i != -1; i = g2[i].next) { int v = g2[i].v; if (dis[v] < dis[u] + g2[i].w) { dis[v] = dis[u] + g2[i].w; if (!inq[v]) { inq[v] = true; q.push(v); } } } } } void solve() { spfa(s);//求最长路 int ans = 0; for (int i = 0; i <= bcnt; ++i) ans = max(ans,dis[i]); printf("%d\n",ans); } int main() { int t; scanf("%d",&t); while (t--) { build1();//根据题意建图 build2();//缩点后建图 solve();//求解 } return 0; }