[关闭]
@Metralix 2018-03-08T14:14:06.000000Z 字数 3960 阅读 821

寒假训练 DIV1(1)dfs及其应用1


A - The Necklace

欧拉路经


题目大意

给你n个珠子,一个珠子分为两半有两种颜色,用1到50来表示50种不同的颜色。把这些珠子串起来,两个紧挨着的珠子要满足一个条件就是接触的那部分颜色要相同

解题思路

给出两个数字是一条边,然后找出任意一个欧拉回路。 
无向图欧拉回路的条件: 
 1. 要联通 
 2. 每个节点度必须是偶数
  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. using namespace std;
  5. int n;
  6. const int N=55;
  7. int mp[N][N],in[N];
  8. void euler(int now)
  9. {
  10. for (int i=1;i<=50;i++)
  11. if (mp[now][i])
  12. {
  13. mp[now][i]--;
  14. mp[i][now]--;
  15. euler(i);
  16. printf("%d %d\n",i,now);
  17. //一定要逆序输出
  18. }
  19. }
  20. int main()
  21. {
  22. int T;
  23. scanf("%d",&T);
  24. for (int TT=1;TT<=T;TT++)
  25. {
  26. memset(mp,0,sizeof(mp));
  27. memset(in,0,sizeof(in));
  28. scanf("%d",&n);
  29. int s;
  30. for (int i=1;i<=n;i++)
  31. {
  32. int u,w;
  33. scanf("%d%d",&u,&w);
  34. if (i==1) s=u;
  35. mp[u][w]++; mp[w][u]++;
  36. in[w]++;
  37. in[u]++;
  38. }
  39. printf("Case #%d\n",TT);
  40. int ff=1;
  41. for (int i=1;i<=50;i++) //点代表的是颜色,我们要判断所有的点
  42. if (in[i]&1)
  43. {
  44. ff=0;
  45. break;
  46. }
  47. if (!ff)
  48. printf("some beads may be lost\n");
  49. else
  50. {
  51. for (int i=1;i<=50;i++)
  52. euler(i);
  53. }
  54. if (TT!=T) puts("");
  55. }
  56. return 0;
  57. }

B - Guess

标签(空格分隔): 拓扑排序


题目大意

对于一个序列a1,a2...an,我们可以计算出一个符号矩阵S,其中Sij为ai+..+aj的正负号。给出符号矩阵,要求输出一个对应的序列。

解题思路

使用连续和转化为前缀和之差的技巧,将前缀和当做一个顶点,那样就能确立边的关系,以及入度数,之后用拓扑排序求解,先着一个入度为0的顶点,删除其相关的边,循环操作。
  1. #include <cstdio>
  2. #include <cstring>
  3. #include <vector>
  4. #include <queue>
  5. #include <algorithm>
  6. using namespace std;
  7. const int maxn = 105;
  8. int N, A[maxn], C[maxn];
  9. char S[maxn];
  10. vector<int> G[maxn];
  11. void addEdge(int u, int v) {
  12. C[v]++;
  13. G[u].push_back(v);
  14. }
  15. void solve () {
  16. int d = 0;
  17. queue<int> Q;
  18. for (int i = 0; i <= N; i++)
  19. if (C[i] == 0) Q.push(i);
  20. while (!Q.empty()) {
  21. int sz = Q.size();
  22. for (int k = 0; k < sz; k++) {
  23. int u = Q.front();
  24. Q.pop();
  25. A[u] = d;
  26. for (int i = 0; i < G[u].size(); i++) {
  27. int v = G[u][i];
  28. C[v]--;
  29. if (C[v] == 0)
  30. Q.push(v);
  31. }
  32. }
  33. d++;
  34. }
  35. }
  36. int main () {
  37. int cas;
  38. scanf("%d", &cas);
  39. while (cas--) {
  40. scanf("%d%s", &N, S);
  41. memset(C, 0, sizeof(C));
  42. for (int i = 0; i <= N; i++) G[i].clear();
  43. int p = 0;
  44. for (int j = 0; j <= N; j++) {
  45. for (int i = j + 1; i <= N; i++) {
  46. if (S[p] == '+')
  47. addEdge(j, i);
  48. else if (S[p] == '-')
  49. addEdge(i, j);
  50. p++;
  51. }
  52. }
  53. solve();
  54. for (int i = 1; i <= N; i++) {
  55. printf("%d%c", A[i]-A[i-1], i == N ? '\n' : ' ');
  56. }
  57. }
  58. return 0;
  59. }

C - Claw Decomposition

标签: 二分图判定


题目大意

 一张无向连通图,每个点连有三条边。询问是否可以将这个图分成若干个爪子,并满足条件:①每条边只能属于一个爪子②每个点属于几个爪子无所谓。输出YES/NO

解题思路

先对题目进行一下分析,要把原图分成若干个“爪”,而每个爪都有三条边,因为题目说明了每条边只能属于一个爪,所以图中边的总数应该是3的倍数,然后从每个点的度数为3,可以得到m*2 == n*3。(m为边数,n为点数),这是对图的边和点数量关系上先进行分析。
  1. #include <cstdio>
  2. #include <algorithm>
  3. #include <vector>
  4. #include <cstring>
  5. using namespace std;
  6. const int maxn = 300 + 5;
  7. int color[maxn];
  8. vector<int> G[maxn];
  9. bool bipartite(int u){
  10. for(int i = 0;i < G[u].size();i++){
  11. int v = G[u][i];
  12. if(color[v] == color[u]) return false;
  13. if(!color[v]){
  14. color[v] = 3 - color[u];
  15. if(!bipartite(v)) return false;
  16. }
  17. }
  18. return true;
  19. }
  20. int main(){
  21. int n,m;
  22. while(scanf("%d",&n)){
  23. if(n == 0) break;
  24. int a,b;
  25. m = 0;
  26. memset(color,0,sizeof(color));
  27. for(int i = 0;i <= n;i++) G[i].clear();
  28. while(scanf("%d%d",&a,&b)){
  29. if(a == 0 && b == 0) break;
  30. G[a].push_back(b); G[b].push_back(a);
  31. m++;
  32. }
  33. color[1] = 1;//记得先把初始节点的color设为1
  34. if(m*2 == n*3 && bipartite(1))
  35. printf("YES\n");
  36. else
  37. printf("NO\n");
  38. }
  39. return 0;
  40. }

D - Cells

标签: 栈模拟DFS


题目大意

给一棵树,每次每次询问一个点是否是另一个点的祖先

解题思路

 我们思考一下深搜建树的过程,先遍历到一个父亲节点,再往下遍历到这个父亲节点的子节点,再往上回溯到该父亲节点。我们发现如果A是B的祖先,A的遍历时间点早于B的遍历时间点早于B的回溯时间点早于A的回溯时间点。
 也就是说我们如果给每个点建立两个时间值(类似Tarjan的思想,预计后天讲Tarjan),第一个时间值是遍历时间点,第二个时间值是回溯时间点,建树的时候预处理出来,那么对于每个询问,就可以O(1)得出结果了(判断一下四个值大小关系)。
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cstdlib>
  5. #include<cmath>
  6. #include<algorithm>
  7. #include<stack>
  8. #define inf 0x7fffffff
  9. using namespace std;
  10. const int maxn=300000+10;
  11. const int M = 20000000+100;
  12. int n,m,pre[M],bac[M],vis[M],dfs_clock;
  13. int an[maxn],c[maxn],sum;
  14. stack<int> S;
  15. void dfs()
  16. {
  17. memset(vis,0,sizeof(vis));
  18. while (!S.empty()) S.pop();
  19. S.push(0);
  20. while (!S.empty())
  21. {
  22. int u=S.top() ;
  23. if (vis[u]==0)
  24. {
  25. vis[u]=1;
  26. pre[u]= ++dfs_clock;
  27. for (int i=an[u]+1 ;i<=an[u]+c[u] ;i++)
  28. {
  29. if (i<n) S.push(i);
  30. else {
  31. pre[i]= ++dfs_clock;
  32. bac[i]= ++dfs_clock;
  33. }
  34. }
  35. }
  36. else if (vis[u]==1)
  37. {
  38. bac[u]= ++dfs_clock;
  39. S.pop();
  40. }
  41. }
  42. }
  43. int main()
  44. {
  45. int t,ncase=1;
  46. int ok=0;
  47. scanf("%d",&t);
  48. while (t--)
  49. {
  50. if (ok) printf("\n");ok=1;
  51. printf("Case %d:\n",ncase++);
  52. memset(pre,0,sizeof(pre));
  53. memset(bac,0,sizeof(bac));
  54. memset(an,0,sizeof(an));
  55. memset(c,0,sizeof(c));
  56. sum=0;
  57. dfs_clock=0;
  58. int a,b;
  59. scanf("%d",&n);
  60. for (int i=0 ;i<n ;i++)
  61. {
  62. scanf("%d",&c[i]);
  63. an[i]=sum;
  64. sum += c[i];
  65. }
  66. dfs();
  67. scanf("%d",&m);
  68. while (m--)
  69. {
  70. scanf("%d %d",&a,&b);
  71. if (pre[a]<pre[b] && bac[a]>bac[b]) printf("Yes\n");
  72. else printf("No\n");
  73. }
  74. }
  75. return 0;
  76. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注