zoukankan      html  css  js  c++  java
  • Codeforces Round #656

    https://codeforces.com/contest/1385

    本来是练习Java的,后面三道题还是上了C++,顺便学了一遍2SAT。这次可以说是拓扑排序专场,E题用了BFS,G题用DFS,各有千秋,记录一下最后三题。

    Problem E

    给出一个有向图的边,两种情况:有的边已经指定了方向(u -> v),有的边待指定方向(u - v)。现尝试为所有边确定方向,使这个图成为DAG。

    tutorial: 简单地进行拓扑排序,若已有环路肯定无法成为DAG,若无则将待指定的方向按拓扑序指向(逆拓扑序可能生成环路)。 

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 typedef pair<int , int> pii;
     5 
     6 const int mxsz = 200000 + 5;
     7 vector<vector<int>> G;
     8 vector<pii> Bi;
     9 int vec2Order[mxsz];
    10 int indeg[mxsz];
    11 
    12 bool topo() {
    13     queue<int> que;
    14     for(int i = 1; i < G.size(); ++i) {
    15         if(indeg[i] == 0)
    16             que.push(i);
    17     }
    18     int topoInd = 0;
    19     while (!que.empty()) {
    20         int cur = que.front();
    21         que.pop();
    22         vec2Order[cur] = topoInd++;
    23 
    24         for(int v : G[cur]) {
    25             indeg[v] --;
    26             if(indeg[v] == 0)
    27                 que.push(v);
    28         }
    29     }
    30     cerr << topoInd << "," << G.size() << endl;
    31     return topoInd + 1 == G.size();
    32 }
    33 
    34 int main() {
    35     int t;
    36     scanf("%d", &t);
    37     while(t--) {
    38         int n, m;
    39         scanf("%d%d", &n, &m);
    40         G.assign(n+1, vector<int>());
    41         Bi.clear();
    42         memset(indeg, 0, sizeof(indeg));
    43 
    44         for(int i = 0 ;i < m; ++i) {
    45             int tmp, u, v;
    46             scanf("%d%d%d", &tmp, &u, &v);
    47             if(tmp) {
    48                 G[u].push_back(v);
    49                 indeg[v] ++;
    50             }
    51             else
    52                 Bi.push_back(make_pair(u,v));
    53         }
    54         if(topo()) {
    55             puts("Yes");
    56             for(int i = 0; i < G.size(); ++i) {
    57                 for(int j = 0;j < G[i].size(); ++j)
    58                     printf("%d %d
    ", i, G[i][j]);
    59             }
    60 
    61             for(int i = 0; i < Bi.size(); ++i) {
    62                 int u = Bi[i].first, v = Bi[i].second;
    63                 if(vec2Order[u] > vec2Order[v])
    64                     swap(u, v);
    65                 printf("%d %d
    ", u, v);
    66             }
    67         } else {
    68             puts("NO");
    69         }
    70 
    71     }
    72     return 0;
    73 }
    Solution

    Problem F

    给出一棵树和数字k,要求每一次刚好删除k个叶子节点,问能删除几次。

    tutorial: 意思上是简单的implementation,直接模拟删除的过程即可。但是实现上需要注意表示删除叶子结点后的树的结构变化,即新的叶子节点生成以及其父节点,以及特例情况。我的实现略微粗糙,可以优化。

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 
     5 const int mxsz = 200000 + 5;
     6 
     7 int cntleaves[mxsz];
     8 vector<vector<int>> G;
     9 set<int> Leaves;
    10 
    11 int main() {
    12     int t;
    13     scanf("%d", &t);
    14     while (t--) {
    15         int n, k;
    16         scanf("%d%d", &n,&k);
    17 
    18         fill(cntleaves, cntleaves + n, 0);
    19         G.assign(n, vector<int>());
    20         Leaves.clear();
    21         for(int i = 0;i < n-1; ++i) {
    22             int u, v;
    23             scanf("%d%d", &u, &v);
    24             u--; v--;
    25             G[u].push_back(v);
    26             G[v].push_back(u);
    27         }
    28 
    29         //special when n = 2, k = 1
    30         if(k == 1) {
    31             printf("%d
    ", n - 1);
    32             continue;
    33         }
    34 
    35         int ans = 0;
    36         queue<int> vec;
    37         for(int i = 0 ;i < G.size(); ++i) {
    38             if(G[i].size() == 1) {
    39                 cntleaves[G[i][0]] ++;
    40                 Leaves.insert(i);
    41             }
    42         }
    43         for(int i = 0; i < n; ++i) {
    44             if(cntleaves[i] > 0)
    45                 vec.push(i);
    46         }
    47         while (!vec.empty()) {
    48             int cur = vec.front();
    49             vec.pop();
    50 
    51             int moves = cntleaves[cur] / k;
    52             cntleaves[cur] %= k;
    53             int cntDelLeaves = moves * k;
    54             int cntDegree = 0;
    55 
    56             if(cntDelLeaves > 0) {
    57                 for(int i = 0 ; i < G[cur].size(); ++i) {
    58                     if(G[cur][i] != -1) {
    59                         cntDegree ++;
    60                         if(Leaves.count(G[cur][i]) && cntDelLeaves) {
    61                             cntDelLeaves --;
    62                             G[cur][i] = -1;
    63                         }
    64                     }
    65                 }
    66                 if(cntDegree - moves * k == 1) {
    67                     for(int i = 0 ;i < G[cur].size(); ++i) {
    68                         if(G[cur][i] != -1) {
    69                             Leaves.insert(cur);
    70                             cntleaves[G[cur][i]] ++;
    71                             vec.push(G[cur][i]);
    72                             break;
    73                         }
    74                     }
    75                 }
    76             }
    77 
    78             ans += moves;
    79         }
    80 
    81         printf("%d
    ", ans);
    82     }
    83     return 0;
    84 }
    Solution

    Problem G

    给出两行数列,可以交换同一列的上下两个数字,问能否将其转换为两行permutation,并要求回答最小交换次数的解。

    这题有意思,由于正在看SMT,Satisfiability,正好通过这题学习2SAT问题的相关算法。

      1 #include <bits/stdc++.h>
      2 
      3 using namespace std;
      4 
      5 typedef pair<int, int> pii;
      6 vector<vector<pii>> indWhere;
      7 set<int> NotChange;
      8 
      9 vector<vector<int>> g,gt;
     10 vector<int> Order;
     11 vector<bool> Used;
     12 vector<int> Comp;
     13 vector<bool> Assign;
     14 int ans;
     15 
     16 inline int TrueVec(int x) {return 2 * x;}
     17 inline int FalseVec(int x) {return 2 * x + 1;}
     18 
     19 void dfs1(int u) {
     20     Used[u] = true;
     21 
     22     for(int v : g[u]){
     23         if(!Used[v])
     24             dfs1(v);
     25     }
     26     Order.push_back(u);
     27 }
     28 
     29 void dfs2(int u, int cc) {
     30     Comp[u] = cc;
     31     for(int v : gt[u]) {
     32         if(Comp[v] == -1)
     33             dfs2(v ,cc);
     34     }
     35 }
     36 
     37 bool solve(int n) {
     38     Order.clear();
     39     Used.assign(2 * n, false);
     40     Comp.assign(2 * n, -1);
     41     Assign.assign(n, false);
     42     ans = 0;
     43 
     44     for(int i = 0 ;i < g.size(); ++i) {
     45         if(!Used[i]) {
     46             dfs1(i);
     47         }
     48     }
     49     int cntComp = 0;
     50     for(int i = 0 ;i < Order.size(); ++i) {
     51         int u = Order[Order.size() - 1 - i];
     52         if(Comp[u] == -1)
     53             dfs2(u, cntComp++);
     54     }
     55 
     56     for(int i = 0 ;i < Comp.size(); i += 2) {
     57         if(Comp[i] == Comp[i+1]) return false;
     58         Assign[i/2] = Comp[i] > Comp[i+1];
     59         if(Assign[i/2]) ans++;
     60     }
     61 
     62     return true;
     63 }
     64 
     65 int main(){
     66     int t;
     67     scanf("%d", &t);
     68     while (t--) {
     69         ans = 0;
     70         int n;
     71         scanf("%d", &n);
     72         indWhere.assign(n, vector<pii>());
     73         for(int i = 0 ;i < 2; ++i) {
     74             for(int j = 0;j < n ; ++j) {
     75                 int tmp;
     76                 scanf("%d", &tmp);
     77                 tmp --;
     78                 indWhere[tmp].push_back(make_pair(i, j));
     79                 if(indWhere[tmp].size() > 2) {
     80                     ans = -1;
     81                 }
     82             }
     83         }
     84         // special
     85         if(ans == -1) {
     86             puts("-1");
     87             continue;
     88         }
     89 
     90         g.assign(2 * n, vector<int>());
     91         gt.assign(2 * n,vector<int>());
     92         NotChange.clear();
     93 
     94         for(int i = 0; i < n; ++i) {
     95             int r0 = indWhere[i][0].first, c0 = indWhere[i][0].second;
     96             int r1 = indWhere[i][1].first, c1 = indWhere[i][1].second;
     97             if(c0 == c1) { // co is false
     98                 NotChange.insert(c0); //
     99                 g[TrueVec(c0)].push_back(FalseVec(c0));
    100 
    101                 gt[FalseVec(c0)].push_back(TrueVec(c0));
    102             } else if(r0 == r1) { // c0 xor c1 is true;
    103                 g[TrueVec(c0)].push_back(FalseVec(c1));
    104                 g[TrueVec(c1)].push_back(FalseVec(c0));
    105                 g[FalseVec(c0)].push_back(TrueVec(c1));
    106                 g[FalseVec(c1)].push_back(TrueVec(c0));
    107 
    108                 gt[FalseVec(c1)].push_back(TrueVec(c0));
    109                 gt[FalseVec(c0)].push_back(TrueVec(c1));
    110                 gt[TrueVec(c1)].push_back(FalseVec(c0));
    111                 gt[TrueVec(c0)].push_back(FalseVec(c1));
    112             } else { // c0 xor c1 is false
    113                 g[TrueVec(c0)].push_back(TrueVec(c1));
    114                 g[TrueVec(c1)].push_back(TrueVec(c0));
    115                 g[FalseVec(c0)].push_back(FalseVec(c1));
    116                 g[FalseVec(c1)].push_back(FalseVec(c0));
    117 
    118                 gt[TrueVec(c1)].push_back(TrueVec(c0));
    119                 gt[TrueVec(c0)].push_back(TrueVec(c1));
    120                 gt[FalseVec(c1)].push_back(FalseVec(c0));
    121                 gt[FalseVec(c0)].push_back(FalseVec(c1));
    122             }
    123         }
    124 
    125 
    126 
    127         if(solve(n)) {
    128             if(ans <= n - NotChange.size() - ans) {
    129                 printf("%d
    ", ans);
    130                 for(int i = 0, cntAns = 0;i < Assign.size(); ++i){
    131                     if(Assign[i] && NotChange.count(i) == 0) printf("%d%c", i+1, cntAns++==ans-1 ?'
    ':' ');
    132                 }
    133             } else {
    134                 ans = n - NotChange.size() - ans;
    135                 printf("%d
    ", ans);
    136                 for(int i = 0, j = 0; i < Assign.size(); ++i) {
    137                     if(Assign[i] == false && NotChange.count(i) == 0)
    138                         printf("%d%c", i+1, j++==ans-1?'
    ':' ');
    139                 }
    140                 if(ans == 0) puts("");
    141             }
    142 
    143         } else {
    144             puts("-1");
    145         }
    146 
    147 
    148     }
    149     return 0;
    150 }
    Bad Answer

    tutorial: 这题按照解2SAT问题可以得到一组解,但未必是最小交换次数的。其实类似这种想法,还是按照联通分量的思想,点与点之间用不同的边值代表其关系(异或和的真假),然后对一个点先随便赋一个值 b ,其所在联通分量的所有其他点的值必然也会确定,这时再贪心的比较最初赋值为~b 的结果是否更优(同一个联通分量其他点的值取逆即可),查完所有联通分量,即可得到解。

      1 #include <bits/stdc++.h>
      2 using namespace std;
      3 
      4 typedef pair<int, int> pii;
      5 
      6 vector<vector<pii>> indWhere;
      7 vector<vector<pii>> G;
      8 vector<bool> Used;
      9 vector<vector<int>> comp;
     10 vector<int> Assign;
     11 vector<int> Ans;
     12 
     13 void dfs1(int u, int cC) {
     14     Used[u] = true;
     15     for(pair<int, int> item : G[u]) {
     16         int v = item.first;
     17         if(!Used[v])
     18             dfs1(v, cC);
     19     }
     20     comp[cC].push_back(u);
     21 }
     22 
     23 void dfs2(int u, bool flag) {
     24     Assign[u] = flag;
     25     for (auto item : G[u]) {
     26         if(Assign[item.first] == -1) {
     27             dfs2(item.first, flag ^ item.second);
     28         }
     29     }
     30 }
     31 
     32 int main() {
     33     int t;
     34     scanf("%d", &t);
     35     while(t--) {
     36         int ans = 0;
     37         int n;
     38         scanf("%d", &n);
     39         indWhere.assign(n, vector<pii>());
     40         for(int i = 0 ;i < 2; ++i) {
     41             for(int j = 0;j < n ; ++j) {
     42                 int tmp;
     43                 scanf("%d", &tmp);
     44                 tmp --;
     45                 indWhere[tmp].push_back(make_pair(i, j));
     46                 if(indWhere[tmp].size() > 2) {
     47                     ans = -1;
     48                 }
     49             }
     50         }
     51         if(ans){
     52             puts("-1");
     53             continue;
     54         }
     55         G.assign(n, vector<pii>());
     56         for(int i = 0 ;i < n; ++i) {
     57             int r0 = indWhere[i][0].first, c0 = indWhere[i][0].second;
     58             int r1 = indWhere[i][1].first, c1 = indWhere[i][1].second;
     59 
     60             if(c0 == c1) continue;
     61 
     62             G[c0].push_back(make_pair(c1, r0 == r1));
     63             G[c1].push_back(make_pair(c0, r0 == r1));
     64         }
     65 
     66         Used.assign(n, false);
     67         comp.clear();
     68         for (int i = 0; i < n; ++i) {
     69             int curComp = comp.size();
     70             comp.push_back(vector<int>());
     71             if(!Used[i])
     72                 dfs1(i, curComp);
     73         }
     74 
     75         Assign.assign(n, -1);
     76         Ans.clear();
     77         for(int i = 0 ;i < comp.size(); ++i) {
     78             int tot = comp[i].size();
     79             if(tot <= 1) continue;
     80 
     81             int cntTrue = 0;
     82             dfs2(comp[i][0], true);
     83             for (int j = 0; j < comp[i].size(); ++j) {
     84                 if(Assign[comp[i][j]])
     85                    cntTrue++;
     86             }
     87             bool flag = cntTrue > tot - cntTrue;
     88             for (int j = 0; j < comp[i].size(); ++j) {
     89                 if(flag^Assign[comp[i][j]])
     90                     Ans.push_back(comp[i][j]);
     91             }
     92         }
     93 
     94         if(Ans.empty()) {
     95             printf("0
    
    ");
     96         } else {
     97             printf("%d
    ", Ans.size());
     98             for (int i = 0; i < Ans.size(); ++i) {
     99                 printf("%d%c", Ans[i] + 1, i == Ans.size() - 1?'
    ':' ');
    100             }
    101         }
    102     }
    103     return 0;
    104 }
    Solution

    只要permutation的每个数字刚好在两行数列中共出现两次,就必然有解。这其实从2SAT构图的过程中也可发现端倪,因为都是异或的关系,改为imperative后所有的 a 与 ~a之间都没有边,没有一对能直接连,这也会使没有一对在同一个联通分量里,整个图就像是一个个联通分量的团,这也解释了为什么会有多解(任何解取逆也是一个解),也解释了为什么按老方法赋值会不行。

    后记

    https://cp-algorithms.com/graph/2SAT.html

    这是一篇讲2SAT讲的不错的文章,实现用Kosaraju's algorithm感觉比Tarjan写起来要更容易些,个人很喜欢利用  transpose graph 的感觉。

  • 相关阅读:
    Struts2+Spring3+Mybatis3开发环境搭建
    spring+struts2+mybatis
    【LeetCode】Populating Next Right Pointers in Each Node
    【LeetCode】Remove Duplicates from Sorted Array
    【LeetCode】Remove Duplicates from Sorted Array II
    【LeetCode】Binary Tree Inorder Traversal
    【LeetCode】Merge Two Sorted Lists
    【LeetCode】Reverse Integer
    【LeetCode】Same Tree
    【LeetCode】Maximum Depth of Binary Tree
  • 原文地址:https://www.cnblogs.com/Kiritsugu/p/13364000.html
Copyright © 2011-2022 走看看