Codevs P3372 选学霸
题目描述 Description
老师想从N名学生中选M人当学霸,但有K对人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的M尽可能接近。
输入输出
输入描述 Input Description
第一行,三个正整数N,M,K。
第2…K行,每行2个数,表示一对实力相当的人的编号(编号为1…N)。
输出描述 Output Description
一行,表示既不让同学们抗议,又与原来的M尽可能接近的选出学霸的数目。(如果有两种方案与M的差的绝对值相等,选较小的一种。)
样例 Sample
样例输入 Sample Input
4 3 2
1 2
3 4
样例输出 Sample Output
2
数据范围及提示 Data Size & Hint
100%的数据N,P<=30000
分析
该题根据题目描述很显然是背包题,因为是要找与m差值绝对值最小的,所以有可能出现在m的左右两侧m个,所以扩大背包体积2倍进行背包,这样从m向左右两面找第一个装满即f[k]=k的背包,因为必须装满时的f[k]才等于人数,而未装满的则为前面装满的人数。
所以先并查集将一组的人合并,在进行2倍体积的01背包
代码如下
program p3372;
var volume,n,m,k,a,b,i,j:longint;
num:array[1..1000] of longint;
father:array[1..1000] of longint;
f:array[0..1000] of longint;
function max(a,b:longint):longint;
begin
if a>b then exit(a);
exit(b);
end;
function find(x:longint):longint;
begin
if father[x]=x
then exit(x);
father[x]:=find(father[x]);
exit(father[x]);
end;
procedure union(a,b:longint);
begin
father[find(father[b])]:=find(father[a]);
num[find(father[a])]:=num[find(father[a])]+num[b];
num[b]:=0;
end;
begin
readln(n,m,k);
for i:=1 to n do
begin
father[i]:=i;
num[i]:=1;
end;
for i:=1 to k do
begin
readln(a,b);
union(a,b);
end;
volume:=2*m;
for i:=1 to n do
begin
if num[i]<>0
then
for j:=volume downto num[i] do
begin
f[j]:=max(f[j],f[j-num[i]]+num[i]);
end;
end;
for i:=0 to m do
begin
if f[m-i]=m-i
then
begin
write(m-i);
break;
end;
if f[m+i]=m+i
then
begin
write(m+i);
break;
end;
end;
end.
评测结果
运行结果
测试点#study1.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#study10.in 结果:AC 内存使用量: 492kB 时间使用量: 251ms
测试点#study2.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#study3.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#study4.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#study5.in 结果:AC 内存使用量: 256kB 时间使用量: 4ms
测试点#study6.in 结果:AC 内存使用量: 256kB 时间使用量: 31ms
测试点#study7.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#study8.in 结果:AC 内存使用量: 368kB 时间使用量: 115ms
测试点#study9.in 结果:AC 内存使用量: 364kB 时间使用量: 147ms
快考试了。。。好担心。。
或许成永别.