【集合划分】解题报告
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进行分组,判断二分结果是否可行,但是后来不知道怎么地就放弃了,然后改写成并查集。
当时的思路是小于二分结果的边都必须在区间内,所以就用并查集把必须分在同一边的加入到并查集了,因为区间连续所以这两个点之间的所有点也加入到这个并查集。
统计区间的数量就数森林中树的数量,数量大于或等于怎么怎么处理,小于又怎么处理。
鉴于当时思路混乱,这个想法没多大验证的价值了。摒弃。
同样是二分+并查集的网上的代码
==================================================================
=====================================================================
另外一种思路:贪心的想法
首先求最小生成树,然后在最小生成树中删边,删去k条较大的边
或者说是加入n-k条较小的边。
连一条边表示将两个元素分在同一个集合中,又因为不存在环(并查集),加入n-k条之后就不能再加了,剩下的边都是集合之间的边,找到最大那一条就行了。
[问题描述]
给定一个集合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); }
相关文章推荐
- POJ - 2406 Power Strings解题报告(KMP,字符串划分成若干连续相同子串)
- Leetcode 86. Partition List 链表划分 解题报告
- NOIP 2001数的划分 解题报告(划分型DP)
- 数的划分(第一篇解题报告)
- NOIP 2001 数的划分 解题报告
- 团体程序设计天梯赛-练习集 L2-005. 集合相似度 解题报告
- TYVJ 1102 单词的划分 解题报告
- SDOI2012 Round1 day2 集合(set)解题报告
- 整数分解和划分 - 兼 ACM PKU POJ 1221 解题报告
- 解题报告 整数划分
- 整数划分解题报告
- Leetcode 410. Split Array Largest Sum 划分数组 解题报告
- 网易2017秋招编程题集合-解题报告
- 整数划分解题报告(DP方法)
- 土地划分 解题报告
- 【九度OJ】题目1178:复数集合 解题报告
- 【集合分组】解题报告
- 【解题报告】uva562_Dividing coins(划分硬币, dp, 01背包)
- 【codevs1779】 单词的划分 解题报告
- 洛谷 1025 数的划分 DP 解题报告