T1:
有N个人,给出他们的名字跟过年收到的每一笔压岁钱,求压岁钱总和最多的童鞋是哪个。
压岁钱为不超过 100000 的整数
每一笔压岁钱中间用至少一个空格隔开
一行s总长不超过 255 个。
数据保证答案唯一
对 30%的数据: n ≤ 3
对 60%的数据: n ≤ 10
对 100%的数据:n ≤ 50
题解:
暴力枚举求出每个童鞋的压岁钱总和然后找最大值,
只需要注意一下字符与数字之间转化的细节即可。
时间复杂度:O(∑length(s))
var
max,i,j,k,l,n:longint;
t,s,d,cmax:string;
begin
readln(n);
for l:=1 to n do
begin
readln(s);
k:=pos(' ',s);
t:=copy(s,1,k-1);
delete(s,1,k);
s:=s+' ';
k:=pos(' ',s);
i:=0;
while k<>0 do
begin
d:=copy(s,1,k-1);
delete(s,1,k);
val(d,j);
i:=i+j;
k:=pos(' ',s);
end;
if i>max then
begin
max:=i;
cmax:=t;
end;
end;
writeln(cmax);
end.
T2:
有N棵树,mxy可以锯掉所有的树比H高的部分(当然,树木不高于 H 米的部分保持不变)。mxy就得到树木被锯下的部分。
例:
一行树的高度分别为 20,15,10 和 17 米
mH为15米,
切割后树木剩下的高度将是 15,15,10 和 15 米
而 mxy 将
从第1棵树得到 5 米,从第4棵树得到 2 米,
共得到 7 米木材。
找出最大的整数高度 H,使得 ta 能得到的木材至少为 M 米。
对于 30%的数据, 1 ≤ N ≤ 10,1 ≤ M ≤ 30;
对于 70%的数据, 1 ≤ N ≤ 1000,1 ≤ M ≤ 10000;
对于 100%的数据,1≤ N ≤ 1000000,1 ≤ M ≤ 2000000000。
每棵树的高度,值均不超过 1 000 000 000
保证所有木材长度之和大于 M,因此必然有解
题解:
二分答案:
直接枚举肯定会炸:
我们发现答案的范围在[0..max{ai}]
所以我们去二分这个范围:
过分满足即>m,我们就向后二分
刚好满足就输出
没满足即 < m,我们就向前二分
如果没有刚好满足的情况,
二分以后的r就是最优的
时间复杂度:O(N* log(max{ai}))
var
a:array [0..1000001] of longint;
l,r,mid,ans,i,h,n:longint;
m,j:int64;
begin
readln(n,m);
for i:=1 to n do
begin
read(a[i]);
if a[i]>r then r:=a[i];
end;
l:=1;
while l<=r do
begin
mid:=(l+r) div 2;
j:=0;
for i:=1 to n do
if a[i]>mid then j:=j+(a[i]-mid);
if j>m then l:=mid+1
else if jthen r:=mid-1
else begin
writeln(mid);
halt;
end;
end;
writeln(r);
end.
T3:
给定一个数字字符串S,用最少次数的加法让字符串等于一个给定的目标数字。每次加 法就是在字符串的某个位置插入一个加号。在需要的所有加号都插入后,就象做普通加法那样来求值。
例:
考虑字符串”12”,做 0 次加法,我们得到数字 12。插入1个加号,我们得到1+2=3。因此,这个例子中,最少用 1 次加法就得到3。
考虑字符串”303”和目标数字6,最佳方法不是”3+0+3”,而是”3+03”。能这样做是因为 1 个数的前导0不会改变它的大小。
对于 16%的数据: 1 ≤ S 的长度 ≤ 5;
对于 80%的数据: 1 ≤ S 的长度 ≤ 10;
对于 100%的数据:1 ≤ S 的长度 ≤ 40,0 ≤ N ≤ 1000。
题解:
这题很多人用了记忆化,不过我用的是DP:
我们设f[i,j]表示S前i位变成了数j最少用了多少次加法。
然后我们很容易得到转移:
f[i,j]=f[k,j-l]+1
很明显,l这个数就是s[k+1..i]这个子串的数字形式!
所以我们用一个数组去预处理这些子串的数
然后就可以推过去了,注意一下初值还有预处理的任意一个数都不能>N即可
时间复杂度:O(S的长度*N)
var
p:array [0..1001,0..1001] of longint;
f:array [0..41,0..1001] of longint;
a:array [0..1001] of longint;
i,j,k,n,m:longint;
s,t:string;
function min(aa,bb:longint):longint;
begin
if aa>bb then exit(bb);
exit(aa);
end;
begin
readln(s);
n:=pos(' ',s);
t:=copy(s,n+1,length(s)-n);
delete(s,n,length(s)-n+1);
n:=length(s);
val(t,m);
for i:=1 to n do
a[i]:=ord(s[i])-48;
for i:=1 to n do
for j:=i to n do
if (p[i,j-1]*10+a[j]<=m) and (p[i,j-1]<>-1)
then p[i,j]:=p[i,j-1]*10+a[j]
else p[i,j]:=-1;
for i:=1 to n do
for j:=0 to m do
f[i,j]:=maxlongint div 5;
if p[1,1]=-1 then
begin
writeln(-1);
halt;
end;
f[1,a[1]]:=0;
for i:=2 to n do
begin
for j:=1 to i-1 do
for k:=0 to m do
if f[j,k]<>maxlongint div 5 then
if p[j+1,i]<>-1 then
if k+p[j+1,i]<=m then
f[i,k+p[j+1,i]]:=min(f[i,k+p[j+1,i]],f[j,k]+1);
if p[1,i]<>-1 then
f[i,p[1,i]]:=0;
end;
if f[n,m]<>maxlongint div 5
then writeln(f[n,m])
else writeln(-1);
end.
T4:
题目大意:
N 个 mxy 正在玩游戏:桌子上有 M 张卡片,这 M 张卡片分别有一个唯一的 1~M 的编号。N 个 mxy 在桌子上抢牌。每个人最后的得分是所得的所有卡片编号的乘积(如果一张卡片都没取,得分为 1)。
然而本体 mxy 把把都输,为了验证自己是不是真的是幸运 E,她决定检验一下是否有人在说谎。
给出T组N,M以及对应的N个Ai表示每个人报出的得分,不可能没有人说谎就输出Yes
可能没有人说谎就输出No
对于 30%的数据: N ≤ 3, M ≤ 10, Ai ≤ 100;
对于 100%的数据: N ≤ 5, M ≤ 100, Ai ≤ 50000, T ≤ 10;
题解:
这题就是裸搜+剪枝:
注意一下对于一个ai,以及我们搜到现在ci的值,
我们可以让它要满足一些条件才能往下做,这就是剪枝:
①因为ai mod ci必定=0,所以我们判断ai div ci mod j=0因为ci*p=ai,那么p必须是j的倍数!
②ci*j≤ai
③每次向后搜,避免重复的枚举!
然后注意标记走过的点去回溯~
var
d:array [0..1001] of boolean;
a:array [0..6] of longint;
c:array [0..6] of longint;
k,i,j,n,m,t:longint;
f:boolean;
procedure dfs(op,dep:longint);
var
i:longint;
begin
if dep>n then f:=true;
if f then exit;
for i:=op to m do
if (a[dep] div c[dep] mod i=0) and (not(d[i])) and (c[dep]*i<=a[dep]) then
begin
c[dep]:=c[dep]*i;
d[i]:=true;
if c[dep]=a[dep]
then dfs(1,dep+1)
else dfs(i+1,dep);
d[i]:=false;
c[dep]:=c[dep] div i;
end;
end;
begin
readln(t);
for k:=1 to t do
begin
readln(n,m);
for i:=1 to m do d[i]:=false;
for i:=1 to n do c[i]:=1;
for i:=1 to n do read(a[i]);
f:=false;
dfs(1,1);
if f then writeln('No')
else writeln('Yes');
end;
end.