NOIP2010 关押罪犯(图论+二分)

考试的时候写的最大生成树,然后二分图染色,因为这样两个矛盾很大的罪犯不会被分在一个监狱里面。

然而最大生成树超时,80分。

正解为二分边权,将边权值大于mid的全部连边构图,判断是否为二分图,如果不是二分图,那么无解。

如果无解,则说明边权的限制条件太小了,因为连的边太多,不容易形成二分图;如果有解,则说明边权的限制条件太大,因为更少的边有利于形成二分图。

80分代码,最大生成树:

#include
#include
#include
#define MAXN 20005
using namespace std;
struct T
{
    int u;
    int v;
    int w;
    bool f;
}a[100005];
bool cmp(T x,T y)
{
    return x.w > y.w;
}
int fa[MAXN];
int find(int x)
{
    if(fa[x] == x) return fa[x];
    else return fa[x] = find(fa[x]);
}
 
struct E
{
    int v;
    int w;
    int next;
    bool used;
}edge[100005];
int cnt,head[MAXN];
void add_edge(int u,int v,int w,bool flag)
{
    edge[cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].used = flag;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}
int n,m;
int belong[MAXN];
int ans;
bool vis[MAXN];
void dfs(int u,int fa)//二分图染色+选边权最大值
{
    belong[u] = (belong[fa]+1)%2;
    vis[u] = 1;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].v;
        if(edge[i].used == 0&&belong[v] != -1&&belong[v] == belong[u])
        {
            ans = max(ans,edge[i].w);
        }
        else if(edge[i].used == 1&&!vis[v])
        {
            dfs(v,u);
        }
    }
}
int main()
{
    //freopen("prison.in","r",stdin);
    //freopen("prison.out","w",stdout);
    memset(head,-1,sizeof head);
    scanf("%d%d",&n,&m);
    for(int i = 0; i < m; i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        a[i].u = x;
        a[i].v = y;
        a[i].w = z;
        a[i].f = 0;
    }
    sort(a,a+m,cmp);
    for(int i = 0; i <= n; i++) fa[i] = i;
    for(int i = 0; i < m; i++)//最大生成树
    {
        int fx = find(a[i].u);
        int fy = find(a[i].v);
        if(fx != fy)
        {
            a[i].f = 1;
            fa[fx] = fy;
            add_edge(a[i].u,a[i].v,a[i].w,1);
            add_edge(a[i].v,a[i].u,a[i].w,1);
        }
    }
    for(int i = 0; i < m; i++)//没有使用的边,如果两个点的连边是这种边而且两个点被染的颜色相同,那么他们是属于一个监狱的,因此答案为符合上述条件的最大边权
    {
        if(a[i].f == 0)
        {
            add_edge(a[i].u,a[i].v,a[i].w,0);
            add_edge(a[i].v,a[i].u,a[i].w,0);
        }
    }
    memset(belong,-1,sizeof belong);
    for(int i = 1; i <= n; i++)
    {
        if(belong[i] == -1)
            dfs(i,0);
    }
    printf("%d\n",ans);
    return 0;
}

满分代码,二分:

#include
#include
#include
#include
#define MAXN 20005
using namespace std;
struct T
{
	int v;
	int next;
}edge[100005];
int head[MAXN],cnt;
void add_edge(int u,int v)
{
	edge[cnt].v = v;
	edge[cnt].next = head[u];
	head[u] = cnt++;
}

struct E
{
	int u;
	int v;
	int w;
	bool operator < (const E &x) const	
	{
		return w > x.w;
	}
}a[100005];
int n,m;
int color[MAXN];
bool flag;
bool dfs(int u)//二分图染色
{
	for(int i = head[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].v;
		if(color[v] == color[u])
		{
			return false;
		}
		else if(color[v] == -1)
		{
			color[v] = (color[u]+1)%2;
			if(dfs(v) == false) return false;
		}
	}
	return true;
}
bool check(int limit)
{
	cnt = 0;
	memset(head,-1,sizeof head);
	memset(color,-1,sizeof color);
	memset(edge,0,sizeof edge);
	for(int i = 0; i < m; i++)//把边权大于限制条件的全部连边
	{
		if(a[i].w <= limit) break;
			add_edge(a[i].u,a[i].v);
			add_edge(a[i].v,a[i].u);
	}
	for(int i = 1; i <= n; i++)
	{	
		if(color[i] == -1)
		{
			color[i] = 0;
			if(dfs(i) == false) return false;//染色不成功,limit太小
		}
	}
	return true;
}
int main()
{
	scanf("%d%d",&n,&m);
	int l,r,mid;
	for(int i = 0; i < m; i++) scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w),r = max(r,a[i].w);
	sort(a,a+m);//这里是否排序对结果没有太大的影响
	l = -1;//注意边界条件
	while(l+1


你可能感兴趣的:(图论)