#include
<
iostream
>
using
namespace
std;
#define
MAX 10000
int
origin[
101
]
=
{
0
};
typedef
struct
range_st {
int
l,r;
} range_st,
*
range_t;
int
ranges_len
=
0
;
range_st ranges[MAX];
range_st temp[MAX];
void
union_range(range_st rg) {
int
i,j,union_count;
for
(i
=
0
;i
<
ranges_len
&&
ranges[i].r
+
1
<
rg.l;i
++
) ;
//
find the first range that can union rg
if
(i
==
ranges_len)
//
no such range found
ranges[ranges_len
++
]
=
rg;
else
if
(ranges[i].r
<
rg.r) {
ranges[i].r
=
rg.r;
}
union_count
=
0
;
for
(j
=
i
+
1
;j
<
ranges_len;j
++
) {
if
(ranges[i].r
+
1
>=
ranges[j].l) {
//
self-union occur
if
(ranges[i].r
<
ranges[j].r)
ranges[i].r
=
ranges[j].r;
union_count
++
;
}
}
ranges_len
-=
union_count;
}
void
update_range(
int
n) {
int
temp_len
=
0
;
range_st rg;
for
(
int
i
=
0
;i
<
ranges_len;i
++
) {
rg.l
=
ranges[i].l
+
n;
rg.r
=
ranges[i].r
+
n;
temp[temp_len
++
]
=
rg;
}
for
(
int
i
=
0
;i
<
temp_len;i
++
)
union_range(temp[i]);
}
void
print_range() {
for
(
int
i
=
0
;i
<
ranges_len;i
++
)
printf(
"
(%d,%d)
"
, ranges[i].l, ranges[i].r);
printf(
"
\n
"
);
}
int
main() {
int
i,j,k;
int
m,n,d,t,ret;
range_st rg;
int
N;
cin
>>
N;
for
(i
=
0
;i
<
N;i
++
)
cin
>>
origin[i];
rg.l
=
rg.r
=
0
;
ranges[ranges_len
++
]
=
rg;
//
init range (0,0)
for
(i
=
0
;i
<
N;i
++
) {
n
=
origin[i];
update_range(n);
//
print_range();
if
(ranges_len
>
1
)
break
;
}
ret
=
ranges[
0
].r
+
1
;
cout
<<
ret
<<
endl;
return
0
;
}
囧死的一题目,给出N个数(N<=100),求一个最小的数,这个数不能是这N个数的任何组合的求和数。
暴力的思维让我去计算所以组合数,根据前i个数生成的所有和数,去计算第i+1个数能够生成的和数,然后把这两堆和数做合并,这可以正确地求出所有可能的和数,但就死活ME,因为数量太大了。注意给出的N个数,每个数最大值是10^6。
在使用第i个数的时候,其实就可以从集合中遍历,查看是否存在一个数少于Ni并且不在集合中,如果是,那么这个就是答案,但写的代码过不了,一直WA 3。
后来换了个思维,通过在草稿上写了些例子,认为这题目应该有很高效的计算方法才是,结果就得出了最后AC的代码。属于0开销代码。
作出的改变是把生成的和数集合中,连续的和数表示成范围,这样处理数据的数量级就大减,并且当发现存在两个不连续的范围后,就能马上得出答案。

