题意:
定义数组A的“符号矩阵”F,F[i][j]表示A[i] + A[i+1] + ... + A[j]。
已知F,给出A的一个可能解。
分析:
1. 利用前缀和。F[i][j[ 表示 S[j] - S[i-1]。
2. 问题转化为已知 S[0] S[1] ... S[n] 的大小关系,并且已知 S[0] = 0,求 S 的可能解,进而可求出A数组。
3. DAG,拓扑排序。涉及三种关系:小于、等于、大于。这里设定结点之间的关系为 <=, u->v,表示 u 小于等于 v。
4. 拓扑序列求出后,利用S[0] = 0,以及“不等作差1处理”,求出S,再求A。
注意:
1. 存储DAG直接使用符号矩阵F,但是现在结点数是 n ,F实际值给出了 G[0...n-1][1...n],即右上部分。
2. j 是 i 的后继结点即 G[i][j] == '+' 而非G[i][j] == '-'。
#include<bits/stdc++.h> using namespace std; const int maxn = 10 + 3; char g[maxn][maxn]; int vis[maxn]; int n; int ans[maxn], cnt; void dfs(int u) { vis[u] = 1; for(int i = 0; i <= n; i++){ if(u != i && !vis[i] && (g[u][i] == '+' || g[u][i] == '0')){ dfs(i); } } ans[cnt--] = u; } void solve() { memset(vis, 0, sizeof(vis)); cnt = n; for(int i = 0; i <= n; i++){ if(!vis[i]) dfs(i); } int s0; for(int i = 0; i <= n; i++){ if(ans[i] == 0){ s0 = i; break; } } //printf("s0: %d\n", s0); int s[maxn]; s[0] = 0; for(int i = s0 - 1; i >= 0; i--){ s[ans[i]] = s[ans[i+1]]; if(g[ans[i]][ans[i+1]] == '+') s[ans[i]]--; } for(int i = s0 + 1; i <= n; i++){ s[ans[i]] = s[ans[i-1]]; if(g[ans[i]][ans[i-1]] == '-') s[ans[i]]++; } printf("%d", s[1]); for(int i = 2; i <= n; i++){ printf(" %d", s[i] - s[i-1]); } printf("\n"); } int main() { int T; cin >> T; while(T--){ cin >> n; char s[maxn*maxn]; scanf("%s", s); int cnt = 0; for(int i = 0; i < n; i++){ for(int j = i + 1; j <= n; j++){ char ch = s[cnt++]; if(ch == '-') {g[i][j] = ch, g[j][i] = '+'; } else if(ch == '+') {g[i][j] = '+', g[j][i] = '-'; } else g[i][j] = g[j][i] = ch; } } for(int i = 0; i <= n; i++) g[i][i] = '0'; solve(); } return 0; }