题目大意:
就是现在给出一个n个点m条边的无向图, 其中有k个点被标记
要求从图中找到尽量多的路径, 这些路径起点和终点互不重合(也就是每个标记点作为起点或者终点只能用一次), 并且没有公共边
输出能找到的最多的路径条数, 以及其中一种方案
大致思路:
首先对原图求出一个生成森林, 于是可以将这个问题转化成在每一个生成树上问连接标记点, 路径没有公共边且每个标记点只能作为起点或者终点用一次, 最多的路径数
容易证明在一棵树上有k的标记点的话最大路径数是k/2, 向下取整, 因为两个路径相交有公共边的话总能找到替换方案, 使得4个点重组变成路径没有公共边
那么对于生成树上找路径的问题, 可以用树形dp解决, 用dp[u]表示当前以u为根的子树下还没有被匹配的点的标号, 进行一遍DP即可找到所有的配对
对于每一个配对求出LCA然后暴力向上爬到LCA把路径输出来即可
代码如下:
Result : Accepted Memory : 15324 KB Time : 93 ms
/* * Author: Gatevin * Created Time: 2015/10/23 9:57:54 * File Name: Sakura_Chiyo.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; #define maxn 50010 #define maxm 50010 int n, m, k; struct Edge { int u, v, nex; Edge(){} Edge(int _u, int _v, int _nex) { u = _u, v = _v, nex = _nex; } }; Edge edge[maxm << 1]; int head[maxn]; int E; void add_Edge(int u, int v) { edge[++E] = Edge(u, v, head[u]); head[u] = E; } int f[maxn];//并查集 int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); } void Union(int x, int y) { int fx = find(x), fy = find(y); if(fx != fy) f[fx] = fy; return; } vector<int> G[maxn];//生成森林 void Kruskal()//寻找生成森林 { for(int i = 1; i <= n; i++) G[i].clear(); for(int i = 1; i <= n; i++) f[i] = i;//初始化并查集 for(int i = 1; i < E; i++) { int u = edge[i].u, v = edge[i].v; int fu = find(u), fv = find(v); if(fu != fv) { G[u].push_back(v); G[v].push_back(u); f[fu] = fv; } } return; } bool die[maxn];//是否标记的点 int dp[maxn];//用dp[u]表示以结点u为根的子树中没有被匹配的那个点 vector<pair<int, int> > ans;//匹配对 bool vis[maxn]; void dfs(int now, int father) { int nex; for(int i = G[now].size() - 1; i >= 0; i--) if((nex = G[now][i]) != father) { if(vis[nex]) continue; vis[nex] = 1; dfs(nex, now); } int match = -1; for(int i = G[now].size() - 1; i >= 0; i--) if((nex = G[now][i]) != father) { if(dp[nex] != -1)//这个子树有需要匹配的点 { if(match == -1) match = dp[nex]; else ans.push_back(make_pair(match, dp[nex])), match = -1; } } if(match == -1 && !die[now]) dp[now] = -1; else if(match == -1 && die[now]) dp[now] = now; else if(match != -1 && die[now]) dp[now] = -1, ans.push_back(make_pair(match, now)); else dp[now] = match; return; } //======== LCA ======== int st[2*maxn + 1][20]; int fa[maxn]; int dfn[maxn]; int pos[maxn]; int sum; void add(int x) { st[++sum][0] = x; pos[x] = sum; return; } void dfs2(int now) { for(int i = 0, sz = G[now].size(); i < sz; i++) { int u = G[now][i]; if(u == fa[now]) continue; dfn[u] = dfn[now] + 1; fa[u] = now; add(now); dfs2(u); } add(now); return; } int Min(int u, int v) { return dfn[u] < dfn[v] ? u : v; } int lca(int u, int v) { u = pos[u], v = pos[v]; if(u > v) swap(u, v); int t = log(v - u + 1.) / log(2.); return Min(st[u][t], st[v - (1 << t) + 1][t]); } void output() { printf("%d\n", (int)ans.size()); vector<int> S; vector<int> V; for(int i = ans.size() - 1; i >= 0; i--) { int x = ans[i].first, y = ans[i].second; int A = lca(x, y); S.clear(); V.clear(); S.push_back(y); while(y != A) y = fa[y], S.push_back(y); while(x != A) V.push_back(x), x = fa[x]; printf("%d ", (int)(S.size() + V.size()) - 1); for(int i = 0, sz = V.size(); i < sz; i++) printf("%d ", V[i]); for(int i = S.size() - 1; i >= 0; i--) printf("%d ", S[i]); putchar('\n'); } return; } void solve() { ans.clear(); memset(vis, 0, sizeof(vis)); //init LCA sum = 0; dfn[1] = 0; fa[1] = -1; for(int i = 1; i <= n; i++) { if(!vis[i]) { vis[i] = 1; dfs(i, -1); dfs2(i);//LCA } } for(int j = 1; (1 << j) <= sum; j++) for(int i = 1; i <= sum - (1 << j) + 1; i++) st[i][j] = Min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); output(); } int main() { scanf("%d %d %d", &n, &m, &k); E = 0; memset(head, -1, sizeof(head));//初始化邻接表 int u, v; for(int i = 0; i < m; i++) { scanf("%d %d", &u, &v); add_Edge(u, v); add_Edge(v, u); } memset(die, 0, sizeof(die)); int x; for(int i = 0; i < k; i++) { scanf("%d", &x); die[x] = 1; } Kruskal();//求生成森林 solve();//接下来进行树形DP return 0; } /* 6 4 4 1 2 2 3 4 5 5 6 1 3 4 6 */