T1:
最大值:
题目大意:
给定n个数,{x1,x2,…,xn}要求从中选出至少一个数,至多n个数,使得乘积之和最大。
对于70%的数据:1 ≤ n ≤ 9
对于100%的数据: 1 ≤ n ≤ 18,-10 ≤xi≤ 10
-10 ≤xi≤ 10
题解:
DP:
虽然貌似排序,搜索那些这数据都能过,不过还是打了个DP.
f[i,1]表示到第i位最小的负数。
f[i,2]表示到第i位的最大乘积。
然后每次对于f[i,1]就判断前一位是否为正数,如果为则重新记录f[i,1]=1,否则f[i,1]=f[i-1,1],即f[i,1]=min(1,f[i-1,1])
对于f[i,2]就判断前面的数是否为负数,如果是就另外寻解
即f[i,2]=max(1,f[i-1,2]) 然后再判断
f[i,1]=min(f[i-1,2]*x,f[i,1]*x,f[i,1])
f[i,2]在此基础上记录max
f[i,2]=max(f[i-1,1]*x,f[i,2]*x,f[i,2])
时间复杂度:O(2N)
var
f:Array [0..19,1..2] of int64;
i,j,n:longint;
p,q,x:int64;
function max(aa,bb:int64):int64;
begin
if aa>bb then exit(aa);
exit(bb);
end;
function min(aa,bb:int64):int64;
begin
if aa>bb then exit(bb);
exit(aa);
end;
begin
assign(input,'max.in'); reset(input);
assign(output,'max.out');rewrite(output);
readln(n);
readln(f[1,1]);
f[1,2]:=f[1,1];
for i:=2 to n do
begin
readln(x);
p:=min(1,f[i-1,1]);
q:=max(1,f[i-1,2]);
f[i,1]:=min(q*x,min(p,p*x));
f[i,2]:=max(p*x,max(q,q*x));
end;
writeln(f[n,2]);
close(input); close(output);
end.
T2:
火柴:
题目大意:
给定一个N位的数(可能会有前导0),将火柴棍重新排列后,能得到的最大的数是多少?
注意不能多出或者少一位, 火柴棍要全部用上.
对于20%的数据:1 ≤ n ≤ 10
对于60%的数据:1 ≤ n ≤ 1000
对于100%的数据: 1 ≤ n ≤ 100000,1 ≤T≤ 10
题解:
贪心:
一开始预处理出0~9要多少个火柴拼,然后枚举出每个数的火柴使用总数.从高到低位,从大到小放数字,注意火柴棒要恰好用完,且位数一样,所以要时刻检查当前火柴棒是否存在符合要求的放置方法,即检查全部放1或者全部放8行不行,因为2占用的火柴少,8占用的火柴多。
const
num:array [0..9] of longint=(6,2,5,5,4,5,6,3,7,6);
var
sum,i,j,k,x,t:longint;
s:ansistring;
d:string;
begin
assign(input,'match.in'); reset(input);
assign(output,'match.out');rewrite(output);
readln(t);
for i:=1 to t do
begin
readln(s);
j:=pos(' ',s);
d:=copy(s,1,j-1);
val(d,x);
delete(s,1,j);
sum:=0;
for j:=1 to length(s) do sum:=sum+num[ord(s[j])-48];
j:=x;
while j>0 do
begin
k:=9;
while ((sum-num[k]>7*(j-1)) or (sum-num[k]<2*(j-1))) do dec(k);
write(k);
sum:=sum-num[k];
dec(j);
end;
writeln;
end;
close(input); close(output);
end.
T3:
游戏:
在1~N内猜随便一个数M,问最少要问几个问题才能猜出,对于每个问题,即M能否被I整除。
对于20%的数据:1 ≤ n ≤ 20
对于60%的数据:1 ≤ n ≤ 1000
对于100%的数据: 1 ≤ n ≤ 100000
题解:
找规律可以发现:
因为是一次性提出m个问题,然后得到回答,所以可以通过问题反向退出最终可能的所有情况。
然后我们发现最坏情况其实就是为所有质数的幂次<=n的个数
然后用塞质数的方法去塞,然后再去暴力枚举一下,注意去重
var
f,p:Array [0..100001] of boolean;
i,n,ans:longint;
j:int64;
begin
assign(input,'game.in'); reset(input);
assign(output,'game.out');rewrite(output);
readln(n);
p[1]:=true;
for i:=2 to n do
if not(p[i])
then begin
j:=2;
while i*j<=n do
begin
p[i*j]:=true;
inc(j);
end;
j:=i;
while j<=n do
begin
if not(f[j]) then
begin
f[j]:=true;
inc(ans);
end;
j:=j*i;
end;
end;
writeln(ans);
close(input); close(output);
end.
T4:
Hack比赛:
ZCC做题得到了C分。除去rating最高的选手Memset137。ZCC想要hack他们来使他的得分更高。除了ZCC,房间里有N个选手,他们已经被按照rating从小到大排序了(所以Memset137是排在第N个的选手)。当ZCC成功hack了第i个选手时,他会获得i分的收入。你可以假设ZCC hack技术高超,百发百中,可以hack除了Memset137和自己以外的所有选手,而且在此期间没有其他选手干扰。
ZCC想要知道,存在着多少种不同的选择一些人hack的方案,使得他的得分在L和R(C≤L≤R < C+N)之间。
显然答案会很大,请输出答案对998244353取模后的结果。
对于20%的数据: 1≤N≤50;
对于50%的数据: 1≤N≤2000;
对于100%的数据: 1≤N≤100000, 1≤C≤1000000,C≤L≤R < C+N;
题解:
DP:
50分:
不难发现,可以把题目概括为从1到n-1选若干个数和在L-C到R-C的方案数,即
设f[i,j]表前i个使得和为j的方案总数,然后推出O(NC)的做法
f[i,j]=f[i-1,j-i]+F[i-1,j]
时间复杂度:O(NC)
AC做法:
然后我们发现数据R < C+N,说明R-C < N,而前m个数的和为m(m-1)/2。
令m(m-1)/2=n,则m只有根号n级别,那么说明最多选根号2n个数
这时我们可以让f[i,j] 表示选了i个数且其中包含i使得和为j的方案总数,即推出
f[i,j]=f[i,j-i]+f[i-1,j-i]
状态1:有i个数,他们的和是j-i,然后因为每个数都不同,所以各自加上1,构成第二种可能。
状态2:有i-1个数,他们的和是j-i,给他们加上一个i,使得有i个数,和为j
这样可以使得空间时间复杂度大大降低
时间复杂度:O(N*sqrt(2n))
const
modn=998244353;
var
f:Array [0.. 448,0..100001] of longint;
i,j,n,c,l,r:longint;
ans:longint;
begin
assign(input,'hack.in'); reset(input);
assign(output,'hack.out');rewrite(output);
readln(n,c,l,r);
l:=l-c; r:=r-c;
f[0,0]:=1;
for i:=1 to trunc(sqrt(n))+131 do
begin
for j:=i to r do
f[i,j]:=(f[i,j-i]+f[i-1,j-i]) mod modn;
for j:=l to r do
ans:=(ans+f[i,j]) mod modn;
end;
writeln(ans);
close(input); close(output);
end.