Codeforces1100E. Andrew and Taxi(二分+dfs判环+拓扑排序)

链接:

        E. Andrew and Taxi

题意:

        给一个有向图,可以修改一些边的方向使得有向图无环,问所修改边的最大权值最小是多少。以及当最大权值最小时最少修改多少条边,修改哪些边。

思路:

        和贩神一起打的这一场,当场自闭,根本不是2200难度的题好嘛。。贩神当场有思路(可能还是太强了吧)

        说到这道题的做法,贩神能够举一反三,以至于以后见到二分答案的题时总会说出“cf原题啊”。

                                                

        言归正传,这道题一拿到手感觉就很复杂,胡思乱想瞎找环,找环里权值最大边之类的伪算法肯定是过不去的。

        这道题的突破口在于:“最大权值最小值为x时可以,那x+1时肯定更可以”。可以据此二分确定最大权值的最小值:如果最大权值最小值为mid时权值>mid的那些边(不可控的边)成环了,那么一定是mid不够大,如果已经没环了,试试mid能不能再减小。

        判环这里用到了dfs判环:设vis有三种状态,0,1,2,0代表未访问,1代表已访问过,2代表正在访问。当dfs到一个2的点时,表示成环了。

        确定了最大权值的最小值(ans)之后,保证小于ans的边始终是拓扑序小的向拓扑序大的边连线,就能保证不成环,toposort一下,将拓扑序相反的边加入vector里,vector的size就是最少要翻转几条边了。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 100010;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-6;
static const double pi = acos(-1);

void redirect(){
    #ifdef LOCAL
        freopen("test.txt","r",stdin);
    #endif
}
struct node{
    int id,v,w;
    node(){}
    node(int _id,int _v,int _w){
        id = _id,v = _v,w = _w;
    }
};
int n,m,ans,cnt;
vector<node>edge[maxn],e[maxn];
int vis[maxn],degree[maxn],order[maxn];
vector<int>out;

bool dfs(int p){
    if(vis[p] == 1)return true;
    if(vis[p] == 2)return false;
    vis[p] = 2;
    bool res = true;
    for(auto& x : e[p])
    if(res)res = min(res,dfs(x.v));
    vis[p] = 1;
    return res;
}

bool nocircle(){
    memset(vis,0,sizeof(vis));
    for(int i = 1;i <= n;i++)e[i].clear();
    for(int i = 1;i <= n;i++)
    for(auto& x : edge[i])
    if(x.w > ans)e[i].push_back(x),degree[x.v]++;
    for(int i = 1;i <= n;i++)
    if(!vis[i] && !dfs(i))return false;
    return true;
}

void topo(){
    memset(degree,0,sizeof(degree));
    queue<int>q;
    for(int i = 1;i <= n;i++)
    for(auto& x : edge[i])
    if(x.w > ans)degree[x.v]++;
    for(int i = 1;i <= n;i++)
    if(!degree[i])q.push(i);
    while(!q.empty()){
        int p = q.front();
        q.pop();
        order[p] = ++cnt;
        for(auto x : edge[p])
        if(x.w > ans && !--degree[x.v])q.push(x.v);
    }
    for(int i = 1;i <= n;i++)
    if(!order[i])order[i] = ++cnt;
}

int solve(){
    topo();
    for(int i = 1;i <= n;i++)
    for(auto& x : edge[i])
    if(x.w <= ans && order[i] > order[x.v])
    out.push_back(x.id);
}

int main(){
    redirect();
    int x,y,z,mx = 0;
    scanf("%d %d",&n,&m);
    for(int i = 1;i <= m;i++){
        scanf("%d %d %d",&x,&y,&z);
        mx = max(mx,z);
        edge[x].push_back(node(i,y,z));
    }
    int l = 0,r = mx;
    while(l < r){
        ans = (l+r)/2;
        if(nocircle())r = ans;
        else l = ans+1;
    }
    ans = l;
    solve();
    int sz = out.size();
    sort(out.begin(),out.end());
    printf("%d %d\n",ans,sz);
    for(int i = 0;i < sz;i++)
    printf("%d%c",out[i],i==sz-1?'\n':' ');
    return 0;
}

猜你喜欢

转载自blog.csdn.net/krypton12138/article/details/87398022
今日推荐