2020NYIST个人积分赛第一场 (二分+拓扑排序)

题意:

一个n个节点的有向图,节点标号从1到n,存在m条单向边。每条单向边有一个权值,代表翻转其方向所需的代价。求使图变成无环图,其中翻转的最大边权值最小的方案,以及该方案翻转的最大的边权。

输入:

单组输入,第一行包含两个整数n和m(2≤n≤100 000,1≤m≤100 000)
接下来m行,每行3个整数,u_i ,v_i ,w_i (1<= u_i , v_i <= n, 1<= w_i <= 10^9),表示u到v有一条权值为w的道路。道路编号从1开始。没有自环。

输出:

在第一行中输出两个整数,即要翻转的最大的边权,和需要反转道路数量k。k不需要是最小的。
在下一行输出k个由空格分隔的整数,表示需要翻转的道路编号
如果有许多解决方案,请打印其中任何一个

样例:

Input
5 6
2 1 1
5 2 6
2 3 2
3 4 3
4 5 5
1 5 4
Output
2 2
1 3
Input
5 7
2 1 5
3 2 3
1 3 3
2 4 1
4 3 5
5 4 1
1 5 3
Output
3 3
3 4 7

思路:

由于图是有向图,并且题目符合二分特性,我们可以用拓扑排序+加二分解决,我们通过二分,枚举边权,将大于mid的边的入度++,然后进行一波拓扑排序,如果拓扑成功,则说明当前的mid可以,所以我们就缩小mid,如果失败则说明当前的mid不行,我们需要扩大mid,因为小于mid的我们可以进行任意的修改,所以,我们就通过这种方式进行不断缩进,得到最小边权中的最大,并在过程中不段更新形成环的边即可

注意:l需要从0开始,因为可能图符合拓扑排序没有最大边权

参考代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
struct node
{
    int to,nex,w,id;
}edge[maxn];
int head[maxn],cnt;
void add(int u,int v,int w,int id)
{
    edge[cnt]=node{v,head[u],w,id};
    head[u]=cnt++;
}
int n,m,k;
int topo[maxn],in[maxn],e[maxn];
bool check(int mid)
{
    queue<int>q;
    memset(in,0,sizeof(in));
    memset(topo,0,sizeof(topo));
    for(int i=1;i<=n;i++)
    {
        for(int j=head[i];~j;j=edge[j].nex)
        {
            if(edge[j].w<=mid)
                continue;
            in[edge[j].to]++;
        }
    }
    for(int i=1;i<=n;i++)
        if(!in[i])
            q.push(i);
    int cnx=0;
    while(!q.empty())
    {
        int u=q.front();
        topo[u]=++cnx;
        q.pop();
        for(int i=head[u];~i;i=edge[i].nex)
        {
            if(edge[i].w<=mid)
                continue;
            int v=edge[i].to;
            in[v]--;
            if(!in[v])
                q.push(v);
        }
    }
    if(cnx!=n)
        return false;
    k=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=head[i];~j;j=edge[j].nex)
        {
            if(edge[j].w>mid)
                continue;
             //根据拓扑序,判断形成环的边加入e{ }
            if(topo[i]>topo[edge[j].to])
                e[k++]=edge[j].id;
        }
    }
    return true;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    memset(head,-1,sizeof head);
    cin>>n>>m;
    int u,v,w;
    int l=0,r=0;
    for(int i=1;i<=m;i++)
       cin>>u>>v>>w,add(u,v,w,i),r=max(r,w);
    int ans=0;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid))
        {
            r=mid-1;
            ans=mid;
        }
        else
        {
            l=mid+1;
        }
    }
    sort(e,e+k);
    cout<<ans<<' '<<k<<endl;
    for(int i=0;i<k;i++)
        cout<<e[i]<<' ';
    cout<<endl;
}
发布了254 篇原创文章 · 获赞 25 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/yangzijiangac/article/details/105263155