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