【NOIP2010提高组】关押罪犯


由题目给出的关系不难建立一个无向图,而题目的要求是将无向图中的点分成两部分,将这两部分中原本互相连接的边删去,求删去后的图的边权最大值.
不难得出算法一:先按边权从小到大排序,贪心地考察每一条边,让较小的”冲突事件”发生(也就是将这两个结点划在同一个部分),然后删去这条边,看能不能构成一个二分图,如果行,则答案就是最后删去的这条边的边权.否则继续删除,直到能形成一个二分图为止.
然而本题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
#include
#include
#include
#include
#define maxn 20005
using 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;kint 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;kint 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();
}

你可能感兴趣的:(图论相关,二分图判定)