您的位置:首页 > 编程语言 > PHP开发

【集合划分】解题报告

2011-10-21 22:00 176 查看
集合划分(Partition)
[问题描述]   

 

    给定一个集合X = {x1, x2, x3…xn}。

    定义函数D[xu, xv]:D[xu, xv]= D[xv, xu]且D[xu, xu] = 0。

    一个partition是指一种将X划分为K个不相交的子集T = (C1, C2…CK)。CP是X的一个非空子集。

定义一个partition的费用Cost(T) = min{D[u, v]},其中u属于Cp、v属于Cq且有p <>q。

[编程任务]

给定N、K和D,求一个划分使其费用最大。

[输入文件]input.txt。

N  K

然后一个N * N的矩阵,第i行j列描述D[i, j]。

[输出文件]output.txt。

你所找到的最大费用。

[样例输入输出]

Input.txt

4 3

0 1 2 3

1 0 2 3

2 2 0 3

3 3 3 0

 

Output.txt

2

[数据约定]

1 < k <= n <= 200

0 <= D[u, v] <= 32000

对于50%的数据满足k <= 10

 

 

 

 

 

 

这道题的模型和关押罪犯几乎一样。关押罪犯两种方法,贪心+并查集,类似于kruskal;二分+染色。当时我同时用了二分和并查集,结果就不知道该怎么做了。

       今天做集合划分又同时用了二分和并查集,WA10。其实考试的时候完全没有找到方向,以为关键是线性表划分,所以想用dfs进行分组,判断二分结果是否可行,但是后来不知道怎么地就放弃了,然后改写成并查集。

       当时的思路是小于二分结果的边都必须在区间内,所以就用并查集把必须分在同一边的加入到并查集了,因为区间连续所以这两个点之间的所有点也加入到这个并查集。

       统计区间的数量就数森林中树的数量,数量大于或等于怎么怎么处理,小于又怎么处理。

       鉴于当时思路混乱,这个想法没多大验证的价值了。摒弃。

 

同样是二分+并查集的网上的代码

==================================================================

type
node=record
t,w:longint;
c:longint;
end;
var
fa:array[0..300] of longint;
d:array[0..300,0..300] of longint;
e:array[1..40000] of node;
i,j,n,k,m,et,tot,ans:longint;
procedure qsort(head,tail:longint);
var i,j:longint; x,temp:node;
begin
i:=head; j:=tail;
x:=e[(i+j) shr 1];
repeat
while(e[j].c>x.c) do dec(j);
while(e[i].c<x.c) do inc(i);
if i<=j then
begin
temp:=e[i];
e[i]:=e[j];
e[j]:=temp;
inc(i); dec(j);
end;
until i>j;
if head<j then qsort(head,j);
if tail>i then qsort(i,tail);
end;

procedure init;
var i,j:longint;
begin
readln(n,k);

for i:=1 to n do for j:=1 to n do d[i,j]:=-1;
for i:=1 to n do
for j:=1 to n do read(d[i,j]);

et:=0;
for i:=1 to n do
for j:=i+1 to n do
begin inc(et); e[et].t:=i; e[et].w:=j; e[et].c:=d[i,j]; end;

qsort(1,et);
end;

function getfather(x:longint):longint;
begin
if fa[x]=x then exit(x);
fa[x]:=getfather(fa[x]);
exit(fa[x]);
end;

procedure union(x,y:longint);
var fx,fy:longint;
begin
fx:=getfather(x);
fy:=getfather(y);
if fx<>fy then
fa[fy]:=fx;
end;

procedure check(mid:longint);
var i,j:longint;
begin

for i:=1 to n do fa[i]:=i;

for i:=1 to mid-1 do
union(e[i].t,e[i].w);

tot:=0;

for i:=1 to n do
if fa[i]=i then inc(tot);
end;

procedure work;
var i,j:longint; head,tail,mid:longint;
begin
tot:=0; ans:=0;
head:=1; tail:=et;
mid:=(1+et) shr 1;
check(mid);
while head<=tail do
begin

if tot<k then
begin
tail:=mid-1;
mid:=(head+tail) shr 1;
check(mid);
end;

if tot>=k then
begin
if tot=k then
begin
if e[mid].c>ans then ans:=e[mid].c; end;
head:=mid+1;
mid:=(head+tail) shr 1;
check(mid);
end;

end;
writeln(ans);
end;
begin
assign(input,'input.txt'); reset(input);
assign(output,'output.txt'); rewrite(output);
init;
work;
close(input);
close(output);
end.


=====================================================================

另外一种思路:贪心的想法

       首先求最小生成树,然后在最小生成树中删边,删去k条较大的边

       或者说是加入n-k条较小的边。

连一条边表示将两个元素分在同一个集合中,又因为不存在环(并查集),加入n-k条之后就不能再加了,剩下的边都是集合之间的边,找到最大那一条就行了。

#include <cstdio>

struct ftv

{

longf;

longt;

longv;

};

ftv bian[40002];

long top=0;

ftv tmp[40002];

long n;long k;

long m = 0;

long fa[40002];

long getroot(long a)

{

if(fa[a]==a) return a;

returnfa[a]=getroot(fa[a]);

}

void mer(long a,long b)

{

fa[getroot(a)]=getroot(b);

}

void insert(long a,long b,long c)

{

bian[++top].f= a;

bian[top].t= b;

bian[top].v= c;

}

void merge(long l,long m,long r)

{

longtop1 = l;

longtop2 = m+1;

for(long i=l;i<r+1;i++)

{

if(top1<m+1&&(top2>r||bian[top2].v>bian[top1].v))

tmp[i]= bian[top1++];

else

tmp[i]= bian[top2++];

}

for(long i=l;i<r+1;i++)

{

bian[i]= tmp[i];

}

}

void merge_sort(long l,long r)

{

if(l>=r) return;

longm = (l+r)>>1;

merge_sort(l,m);

merge_sort(m+1,r);

merge(l,m,r);

}

int main()

{

freopen("partition.in","r",stdin);

freopen("partition.out","w",stdout);

scanf("%ld%ld",&n,&k);

for(longi=1;i<n+1;i++)

{

fa[i]= i;

for(long j=1;j<n+1;j++)

{

longc;

scanf("%ld",&c);

if(i>=j) continue;

insert(i,j,c);

}

}

m= n*n-n;

m/=2;

merge_sort(1,m);

longj = 1;

for(long i=1;i<n+1-k;i++)

{

while(getroot(bian[j].f)==getroot(bian[j].t))j++;

mer(bian[j].f,bian[j].t);

j++;

}

while(getroot(bian[j].f)==getroot(bian[j].t))j++;

printf("%ld",bian[j].v);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息