【NOIP2010提高组】关押罪犯

来源:互联网 发布:大型数据机房施工方案 编辑:程序博客网 时间:2024/05/16 12:18

这里写图片描述
由题目给出的关系不难建立一个无向图,而题目的要求是将无向图中的点分成两部分,将这两部分中原本互相连接的边删去,求删去后的图的边权最大值.
不难得出算法一:先按边权从小到大排序,贪心地考察每一条边,让较小的”冲突事件”发生(也就是将这两个结点划在同一个部分),然后删去这条边,看能不能构成一个二分图,如果行,则答案就是最后删去的这条边的边权.否则继续删除,直到能形成一个二分图为止.
然而本题N和M的取值都很大,这种做法是否能通过全部数据呢?
这种算法的时间复杂度为O(n*(n+m))不能通过所有的数据,需要优化!
注意到题目求解的是最大值最小,而且明确规定了cj的范围,想到二分答案.
故在[0,10^9]内二分答案,将答案带入原图检验,看是否可行即可。
实现时在BFS()中添加一个参数p,BFS(s,p)表示以s为源点,所有边权大于p的点相连是否能形成一个二分图。是为1,否则返回0。
时间复杂度为O(㏒2(10^9)*(n+m))≈O(30*(n+m))
进一步优化:区间的右端点不一定要是上限10^9,只需是所给边权的最大值即可,故可以先预查找最大值.然后再猜.
贴上代码(我们学校跑STL队列慢,所以手写的队列)

#include<cstdio>#include<iostream>#include<cstring>#include<queue>#include<vector>#define maxn 20005using namespace std;int n,m;vector<int>g[maxn],w[maxn];int color[maxn];int q[maxn],front,rear;void init(){    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++)    {        int a,b,c;        scanf("%d%d%d",&a,&b,&c);        g[a].push_back(b);        w[a].push_back(c);        g[b].push_back(a);        w[b].push_back(c);    }}bool BFS(int s,int p){    rear=front=1;    q[rear++]=s;    color[s]=1;    while(front!=rear)    {        int i=q[front++];        for(int k=0;k<g[i].size();k++)        {            int j=g[i][k],c=w[i][k];            if(c<=p)continue;            if(color[j]==color[i])return 0;            if(color[j]==0)            {                color[j]=3-color[i];                q[rear++]=j;            }        }    }    return 1;}bool check(int p){    memset(color,0,sizeof(color));    int ok=1;    for(int i=1;i<=n;i++)if(color[i]==0)    {        ok=BFS(i,p);        if(!ok)return false;    }    return true;}void solve(){    int B=0;    for(int i=1;i<=n;i++)    for(int k=0;k<w[i].size();k++)    B=max(B,w[i][k]);    int A=0,ans;    while(A<=B)    {        int C=(A+B)/2;        if(check(C))        {            ans=C,B=C-1;        }        else        {            A=C+1;        }    }    printf("%d",ans);}int main(){    //freopen("my.in","r",stdin);    //freopen("my.out","w",stdout);    init();    solve();}
0 0
原创粉丝点击