Little Pony and Summer Sun Celebration CodeForces #453C 题解[C++]

18 篇文章 0 订阅
6 篇文章 0 订阅

题目来源


http://codeforces.com/contest/453/problem/C

题意理解


Twilight Sparkle wanted to track the path of Nightmare Moon. Unfortunately, she didn’t know the exact path. What she knew is the parity of the number of times that each place Nightmare Moon visited. Can you help Twilight Sparkle to restore any path that is consistent with this information?

Ponyville can be represented as an undirected graph (vertices are places, edges are roads between places) without self-loops and multi-edges. The path can start and end at any place (also it can be empty). Each place can be visited multiple times. The path must not visit more than 4n places.

Input

The first line contains two integers n and m (2 ≤ n ≤ 105; 0 ≤ m ≤ 105) — the number of places and the number of roads in Ponyville. Each of the following m lines contains two integers ui, vi (1 ≤ ui, vi ≤ n; ui ≠ vi), these integers describe a road between places ui and vi.

The next line contains n integers: x1, x2, …, xn (0 ≤ xi ≤ 1) — the parity of the number of times that each place must be visited. If xi = 0, then the i-th place must be visited even number of times, else it must be visited odd number of times.

Output

Output the number of visited places k in the first line (0 ≤ k ≤ 4n). Then output k integers — the numbers of places in the order of path. If xi = 0, then the i-th place must appear in the path even number of times, else i-th place must appear in the path odd number of times. Note, that given road system has no self-loops, therefore any two neighbouring places in the path must be distinct.

If there is no required path, output -1. If there multiple possible paths, you can output any of them.

Examples
Input
3 2
1 2
2 3
1 1 1
Output
3
1 2 3
Input
5 7
1 2
1 3
1 4
1 5
3 4
3 5
4 5
0 1 0 1 0
Output
10
2 1 3 4 5 4 5 4 3 1
Input
2 0
0 0
Output
0

这是一道有趣的题, 关于小马宝莉–暮光闪闪和露娜公主的故事. 抛开背景不论, 题目给出一个没有多重边和环的图G(V,E)=G(n,m). 同时给出n个顶点所对应的奇偶性, 0为偶, 奇为1. 要求找到一条路径, 长度小于4*n, 使得这条路径对于图中每个顶点的经过次数恰好满足先给出的奇偶性. 出发始点和终点不限, 每个顶点遍历次数不限.

解题思路


这道题想了挺久写了一个毛毛躁躁, 没有剪枝的深搜, 结果交上去之后TLE. 最后还是参考了一些其他的博客才找到解题方法.

最终的思路有些违反直觉, 可以用逐步来理解.

题目要求输出的路径对每个顶点遍历的次数的奇偶性都符合要求. 利用等价转换的逆向思想, 相当于把每个顶点都标上1或者0. 每当路径走过一个顶点, 就把这个顶点的标记取反(0改1, 1改0). 我们的目的相当于要找到一条路径, 把图中所有标记为1的顶点改成标记为0.

先假定我们的深度优先递归函数DFS能够将它遍历过的顶点的标记都改成0, 这是设计递归函数时常用的黑箱思想. 假设现在处于深度搜索的某个节点x, 那么从这个节点x出发, 依次DFS遍历还没有被遍历过的, 与它相邻的顶点x1, x2… 那么当遍历完成之后, 节点x的所有邻结点都已经符合了要求–标记为0–依据我们对递归函数的假设.

现在, 我们需要考虑节点x的奇偶性是否满足要求–即要把它的标记变为0. 假如遍历完它的邻节点返回时, 它的标记恰好变成了0, 自然不需要做任何事, 返回给上一层深搜即可. 但假如它的标记为1, 为了把它的标记变成0, 设它的父节点(它上一层深搜的节点)为y. 那么通过x->y->x走一遍, 它就把自己的标记也变成了0.

注意到, 这样虽然把麻烦甩给了父节点, 但此时已经满足了我们先前对DFS函数的要求: 将它遍历过的结点的标记都改成0.

依据上面的思想, 我们就不难写出我们的代码.

这里还有一个细节, 即要求路径的长度不能大于4*n. 但写完代码我们就会发现, 我们DFS找出来的路径长度不可能大于4*n. 这是因为对于最坏情况, 一个根节点x直接连接若干个叶节点的x1, x2…xk的图, 这个图要求每个顶点的标记都为0. 根据我们的算法在每条边之间最多只会来回两次, 即求出的路径为: x->x1->x1->x....x->xk->xk->x

另外, 从任一个标记为 1 的顶点出发, 结果都是一样的. 具体证明可以看本文的参考博客(https://blog.csdn.net/seven_jun/article/details/51911216).

代码实现


#include <iostream>
#include <vector>
#include <queue>

using namespace std;

int n, m, temp1, temp2;
vector<vector<int>> maze;
vector<int> target;
vector<int> visit;
vector<int> paths;

void dfs(int current, int father)
{
    //从父节点走到当前位置
    visit[current] = 1;
    paths.push_back(current);
    target[current] ^= 1;

    for (int i = 0; i < maze[current].size(); i++){
        if (!visit[maze[current][i]]) {
            dfs(maze[current][i], current);
            //走完邻节点了, 再走一步从邻节点回到目前位置
            paths.push_back(current);
            target[current] ^= 1;
        }
    }
//站在现在的位置, 发现这时的标记为1, 通过走一圈父节点来变回0
    if (father != -1 && target[current]==1) {
        paths.push_back(father);
        paths.push_back(current);
        target[father] ^= 1;
        target[current] ^= 1;
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    visit = vector<int>(n, 0);
    target = visit;
    maze = vector<vector<int>>(n, vector<int>());

    for (int i = 0; i < m; i++) {
        scanf("%d%d", &temp1, &temp2);
        temp1--;
        temp2--;
        maze[temp1].push_back(temp2);
        maze[temp2].push_back(temp1);
    }

    int start = -1;
    for (int i = 0; i < n; i++) {
        scanf("%d", &target[i]);
        if (target[i] == 1) start = i;
    }

    if (start != -1) dfs(start,-1);
    //没有标记为1的节点, 这个图不需要走就满足了要求
    else {
        printf("0\n");
        //system("PAUSE");
        return 0;
    }

//用这种回溯法, 第一个走的节点会有误差, 这里进行矫正
    if (target[start]) {
        target[start] = 0;
        paths.pop_back();
    }

    for (int i = 0; i < n; i++) {
        if (target[i] == 1) {
            printf("-1\n");
            //system("PAUSE");
            return 0;
        }
    }

    printf("%d\n", paths.size());
    for (int i = 0; i < paths.size(); i++)
        printf("%d ", paths[i]+1);

    //system("PAUSE");
    return 0;

}

代码表现


p2_ac

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值