CodeForces - 219D Choosing Capital for Treeland--遍历+找规律

本文介绍了一种求解有向图中从任意一点出发到达其他所有点所需的最小翻转次数的方法。通过一次遍历,利用栈结构实现路径追踪,避免了暴力求解,实现了高效计算。

题意:给定一个有向图,从一个节点出发到所有的节点需要翻转多少次方向,问最小翻转次数为多少次

条件:1 如果是无向图,这整个图都是通的

            2 题目给出的n节点的个数是2·10^5

思路:题目给出的n节点的个数是2·10^5,所以存地图的时候要用vector,而且绝对不能暴力。想必肯定只用遍历一次就可以得到其它点的关系,以此想法为突破口,就会发现我任选一个起始点,只要遍历起始点到其他所有的顶点的到翻转次数和起始点到该个点需要的翻转和不翻转次数。其它点的到其它所有点翻转次数=起始点到其他所有的顶点的到翻转次数-起始点到该个点需要的翻转次数+起始点到该个点需要的不翻转次数。(ans[i]=count1-vis[i][1]+vis[i][3])

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <cstring>
using namespace std;
const int maxn=200005;
int count1;//总逆置步数
vector<int>num[maxn*2];//地图
int vis[maxn][4];//记录有没有办拜访过,起始点到该店翻转数和不翻转数
int ans[maxn];//统计每一个节点翻转数
int main()
{
    int n;
    scanf("%d",&n);
    memset(vis,0,sizeof(vis));
    count1=0;
    for(int i=0;i<n-1;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        num[x].push_back(y);
        num[y+n].push_back(x);
    }
    vis[1][0]=1;//有没有来过
    vis[1][1]=0;//从选定起点到这里逆置数
    //原先这里有vis[1][2]但其实2不起作用的,所以没有必要
    vis[1][3]=0;//不逆置数目
    stack<int>s;
    s.push(1);
    int numcount=1;
    while(numcount<n)//遍历
    {
        int i;
        i=s.top();
        s.pop();
        for(int j=0;j<num[i].size();j++)
        {
            int x=num[i][j];
            if(vis[x][0]==1)//如果已经访问过
            continue;
            vis[x][0]=1;
            vis[x][1]=vis[i][1];
            vis[x][3]=vis[i][3]+1;
            s.push(x);
            numcount++;
        }
        for(int j=0;j<num[i+n].size();j++)
        {
            int x=num[i+n][j];
            if(vis[x][0]==1)
            continue;
            vis[x][0]=1;
            vis[x][1]=vis[i][1]+1;
            vis[x][3]=vis[i][3];
            count1++;
            s.push(x);
            numcount++;
        }
    }
    int min1=count1;
    ans[1]=count1;
    for(int i=1;i<=n;i++)
    {
        ans[i]=count1-vis[i][1]+vis[i][3];
        min1=min(ans[i],min1);
    }
    printf("%d\n",min1);
    for(int i=1;i<=n;i++)
    {
        if(ans[i]==min1)
        printf("%d ",i);
    }
    return 0;
}

总结:1 那次题目做的还是不错,挺多人没做出来,想法真的挺重要的,但也需要平时多多积累

            2 看了队里大家补题的记录,别人都那么努力,凭什么自己不加把劲

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值