题目:
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=72618#problem/D
题意:
求强连通分量中最大团.
思路:
SCC模板, 缩点得到SCC图, 题目转化为求SCC图上权最大的路径. 因为SCC图是一个DAG, 可以用dp来求解.
dp有点像拓扑排序的思路, 将出度为零的点放入队列,返方向进行求解,更新节点值.
AC.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
using namespace std;
const int MAX = 1005;
vector<int> G[MAX];
int pre[MAX], lowlink[MAX], sccno[MAX], dfs_clock, scc_cnt;
stack<int> S;
int val[MAX], n;
void dfs(int u)
{
pre[u] = lowlink[u] = ++dfs_clock;
S.push(u);
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(!pre[v]) {
dfs(v);
lowlink[u] = min(lowlink[u], lowlink[v]);
}
else if(!sccno[v]) {
lowlink[u] = min(lowlink[u], pre[v]);
}
}
if(lowlink[u] == pre[u]) {
scc_cnt++;
while(1) {
int x = S.top(); S.pop();
val[scc_cnt]++;
sccno[x] = scc_cnt;
if(x == u) break;
}
}
}
void find_scc()
{
dfs_clock = scc_cnt = 0;
memset(sccno, 0, sizeof(sccno));
memset(pre, 0, sizeof(pre));
memset(val, 0, sizeof(val));
for(int i = 0; i < n; ++i) {
if(!pre[i]) dfs(i);
}
}
bool f[MAX][MAX];
vector<int> V[MAX];
int dp[MAX], out[MAX];
void deal()
{
memset(f, 0, sizeof(f));
memset(dp, 0, sizeof(dp));
memset(out, 0, sizeof(out));
for(int u = 0; u < n; ++u) {
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
int uu = sccno[u], vv = sccno[v];
if(uu != vv && f[uu][vv] == 0) {
f[uu][vv] = 1;
out[uu]++;
V[vv].push_back(uu);
}
}
}
}
queue<int> que;
void solve()
{
deal();
while(que.size()) que.pop();
for(int i = 1; i <= scc_cnt; ++i) {
if(out[i] == 0) {
que.push(i);
dp[i] = val[i];
}
}
int ans = 0;
while(que.size()) {
int p = que.front(); que.pop();
if(V[p].size() == 0) {
ans = max(ans, dp[p]);
continue;
}
for(int i = 0; i < V[p].size(); ++i) {
int v = V[p][i];
dp[v] = max(dp[v], dp[p] + val[v]);
out[v]--;
if(out[v] == 0) que.push(v);
}
}
printf("%d\n", ans);
return;
}
int main()
{
//freopen("in", "r", stdin);
int T;
scanf("%d", &T);
while(T--) {
int m;
scanf("%d %d", &n, &m);
for(int i = 0; i <= n; ++i) { //这里应该是有'='的,因为scc_cnt是从1开始记录.
G[i].clear();
V[i].clear();
}
for(int i = 0; i < m; ++i) {
int u, v;
scanf("%d %d", &u, &v);
u--; v--;
G[u].push_back(v);
}
find_scc();
solve();
}
return 0;
}
DP那一块也可以这么写. DAG图上的动态规划可以用递归.
int solve(int x)
{
if(V[x].size() == 0) return dp[x] = val[x];
if(dp[x]) return dp[x];
int cnt = 0;
for(int i = 0; i < V[x].size(); ++i) {
cnt = max(cnt, solve(V[x][i]));
}
return dp[x] = val[x] + cnt;
}
最长上升子序列可以看成DAG上的动态规划问题. 顺便附代码.
#include <iostream>
#include <cstdio>
using namespace std;
int a[1000], b[1000];
int n, m;
int lca(int i, int j)
{
if(i >= n || j >= m) return 0;
if(a[i] == b[j]) {
return lca(i+1, j+1)+1;
}
else {
if(lca(i+1, j) < lca(i, j+1)) return lca(i, j+1);
return lca(i+1, j);
}
}
int main()
{
freopen("in", "r", stdin);
while(~scanf("%d %d", &n, &m)) {
for(int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
for(int i = 0; i < m; ++i) {
scanf("%d", &b[i]);
}
int ans = lca(0, 0);
printf("%d\n", ans);
}
return 0;
}