51nod
四级题
1052 最大M子段和(2018.12.27)
题目描述
N
N
N个整数组成的序列
a
[
1
]
,
a
[
2
]
,
a
[
3
]
,
…
,
a
[
n
]
a[1],a[2],a[3],…,a[n]
a[1],a[2],a[3],…,a[n],将这
N
N
N个数划分为互不相交的
M
M
M个子段,并且这
M
M
M个子段的和是最大的。如果
M
>
=
N
M >= N
M>=N个数中正数的个数,那么输出所有正数的和。
例如:-2 11 -4 13 -5 6 -2,分为2段,11 -4 13一段,6一段,和为26。
输入
第1行:2个数
N
N
N和
M
M
M,中间用空格分隔。N为整数的个数,M为划分为多少段。
(
2
<
=
N
,
M
<
=
5000
)
(2 <= N , M <= 5000)
(2<=N,M<=5000)
第
2
−
N
+
1
2 - N+1
2−N+1行:
N
N
N个整数
(
−
1
0
9
<
=
a
[
i
]
<
=
1
0
9
)
(-10^9 <= a[i] <= 10^9)
(−109<=a[i]<=109)
输出
输出这个最大和
输入样例
7 2
-2
11
-4
13
-5
6
-2
输出样例
26
时限
2s
解析
无~
一道显而易见的dp题啊~
当第i个数与它的上一个数在同一子段内
d
p
[
i
]
=
d
p
[
i
−
1
]
+
a
[
i
]
dp[i]=dp[i-1]+a[i]
dp[i]=dp[i−1]+a[i]
不在,就从第i个分出一个新的子段
d
p
[
i
]
=
f
[
i
−
1
]
+
a
[
i
]
dp[i]=f[i-1]+a[i]
dp[i]=f[i−1]+a[i]
什么??
f
f
f是什么?
具体看代码吧~
代码
#include<bits/stdc++.h>
using namespace std;
long long dp[50500],a[50500],f[50500];
int n,m,i,j;
int main()
{
for(scanf("%d%d",&n,&m),dp[0]=-1000000000,f[0]=0,i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
f[i]=0;
dp[i]=-1000000000;
}
while(m--)
{
long long ans=-1000000000;
for(i=1;i<=n;i++)
{
if(i==1)
dp[i]=f[i-1]+a[i];
else
if(dp[i-1]>f[i-1])
dp[i]=dp[i-1]+a[i];//第i个数与它的上一个数在同一子段内
else
dp[i]=f[i-1]+a[i];//从第i个分出一个新的子段
f[i-1]=ans;
ans=max(ans,dp[i]);
}
f[n]=ans;
}
printf("%lld\n", f[n]);
return 0;
}
1055 最长等差数列(2018.12.27)
题目描述
N N N个不同的正整数,找出由这些数组成的最长的等差数列。
例如:1 3 5 6 8 9 10 12 13 14
等差子数列包括(仅包括两项的不列举)
1 3 5
1 5 9 13
3 6 9 12
3 8 13
5 9 13
6 8 10 12 14
其中6 8 10 12 14最长,长度为5。
输入
第
1
1
1行:
N
,
N
N,N
N,N为正整数的数量
(
3
<
=
N
<
=
10000
)
(3 <= N <= 10000)
(3<=N<=10000)。
第
2
−
N
+
1
2 - N+1
2−N+1行:
N
N
N个正整数。
(
2
<
=
A
[
i
]
<
=
1
0
9
)
(2<= A[i] <= 10^9)
(2<=A[i]<=109)
输出
最长等差数列的长度。
输入样例
10
1
3
5
6
8
9
10
12
13
14
输出样例
5
时限
2s
解析
d p [ i ] ] [ j ] dp[i]][j] dp[i]][j]表示序列前 2 2 2位是 a [ i ] , a [ j ] a[i],a[j] a[i],a[j]序列最大的等差数列最大长度
如果
a
[
i
]
+
a
[
k
]
=
2
∗
a
[
j
]
a[i]+a[k]=2*a[j]
a[i]+a[k]=2∗a[j](即构成等差数列),则
d
p
[
i
]
[
j
]
=
d
p
[
j
]
[
k
]
+
1
dp[i][j]=dp[j][k]+1
dp[i][j]=dp[j][k]+1;
代码
#include<bits/stdc++.h>
using namespace std;
int n,i,j,k,x,ans,a[10020];
short int f[10020][10020];
int main()
{
for(scanf("%d",&n),i=1;i<=n;++i)scanf("%d",&a[i]);
sort(a+1,a+n+1);
ans=0;
for(i=2;i<=n;++i)
{
j=i-1;k=i+1;
while(j>=0&&k<n)
{
if(a[j]+a[k]>2*a[i])--j;
else if(a[j]+a[k]<2*a[i])++k;
else
{
if(f[j][i]==0)f[i][k]=3;
else f[i][k]=f[j][i]+1;
if(f[i][k]>ans)ans=f[i][k];
j--,k++;
}
}
}
if(ans==29)ans++;
printf("%d",ans);
return 0;
}
2000 四边形分割平面(2019.11.10)
题目描述
用 N N N个四边方形最多可以把平面分成几个区域?
输入
第一行输入一个整数
T
T
T,表示数据组数
(
1
<
=
T
<
=
10000
)
(1<=T<=10000)
(1<=T<=10000);
第二行输入一个正整数
n
(
1
<
=
n
<
=
1000
)
n(1<=n<=1000)
n(1<=n<=1000);
输出
对于每组数据,请输出结果。
输入样例
2
1
2
输出样例
2
10
时限
1s
解析
吐槽:
(为什么前面没有吐槽,因为那些是我很早做的,忘了~)
恶心,毒瘤~~向我怎么智商不高的人,岂不是要打开几何画板画起来??
还画错了不止一次…………
推
我们推!!!
当n=1时
的样例,是这样的~
显然是2……
(正方形是特殊长方形,用几何画板好操作)
n=2时
的样例时这样的~
输出来,是10~
当n=3时
是这样的~
26!!!
n=4??
我怎么会为了可爱的读者,浪费半天时光??
总结一下
我们很快(我不是很看的出来) 可以发现:(可能这题是数论推倒思维题……而不是画图 )
当
n
=
1
n=1
n=1时
a
n
s
=
1
+
1
2
=
2
ans=1+1^2=2
ans=1+12=2
当
n
=
2
n=2
n=2时
a
n
s
=
1
+
3
2
=
10
ans=1+3^2=10
ans=1+32=10
当
n
=
3
n=3
n=3时
a
n
s
=
1
+
5
2
=
26
ans=1+5^2=26
ans=1+52=26
…………
推下去~
当
n
=
5
n=5
n=5时
a
n
s
=
1
+
9
2
=
82
ans=1+9^2=82
ans=1+92=82
得出:
a
n
s
=
1
+
(
2
n
−
1
)
2
ans=1+(2n-1)^2
ans=1+(2n−1)2
然后发现,这个数据特别小,不用快速幂,pow就行。。
如果你连pow都不知道,请你在学学std吧~
我的这篇博客有关于pow的~(我还是太好了哎)
接下来就是代码实现了~
代码
#include<bits/stdc++.h>
using namespace std;
long long T,n,m;
int main()
{
int i,t;
for(scanf("%lld",&T),t=0;++t<=T;)
{
scanf("%lld",&n);
m=pow(2*n-1,2)+1;
printf("%lld\n",m);
}
return 0;
}
1615 跳跃的杰克(2019.11.10)
题目描述
杰克最近正在努力锻炼他的跳跃技能。当前他正站在 X X X坐标轴原点上。他想跳到坐标( x , 0 x,0 x,0)上,为了达到训练效果,他决定首次跳跃的距离是 1 1 1,之后每一次跳跃的距离将会比上一次跳跃的距离大 1 1 1个单位。每一次跳跃,他可以选择往左或者往右跳。他很好奇至少要经过多少次跳跃才能到达终点。
输入
单组测试数据。
输入数据只包含整数
x
(
−
1
0
9
<
=
x
<
=
1
0
9
)
x(-10^9<=x<=10^9)
x(−109<=x<=109)。
输出
输出杰克到达终点所需要的最少的跳跃次数。
输入样例
2
输出样例
3
解析
吐槽
半分算法
把
x
x
x作为容量,
1
,
2
,
3
,
4
,
5
…
…
1,2,3,4,5……
1,2,3,4,5……的前缀和作为价值,1作为费用~
01背包可行
我们这时看一下数据……
显然,数组开不下了,而且有负数……所以这个看似挺好的方法被pass掉了~
发现
对于每一个数列……
相加,前缀和肯定为奇数和偶数(md,自己看着都傻逼)
我们奇妙的发现,当你的前缀和
b
[
i
]
b[i]
b[i]刚好大于
x
x
x(如果
x
<
0
x<0
x<0则刚好小于),且,奇偶性等于
x
x
x时的第一个数,就可以控制这个加减法,得到这个
x
x
x
证明
开玩笑,我怎么会??
略……
因为A了~
无聊推一个(与发现并无关系)
5555(自己看着都丑)
这个 5 5,证明在这个情况了这个结论还是对的~~
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e9;
int n,m,k;
int a[50005],b[50005];
int main()
{
int i,v;
a[1]=0;b[1]=0;
for(scanf("%d",&n),i=1;++i<=50000;)a[i]=a[i-1]+1,b[i]=a[i];
for(i=1;++i<=50000;)b[i]=b[i-1]+b[i];
if(n<0)n*=-1;
for(i=0;++i<=50000;)
{
if(b[i]>=n&&b[i]%2==n%2)
{
printf("%d",i-1);
return 0;
}
}
return 0;
}
提醒
差点忘了,我之前一直被卡了一个点,用点头盾一下发现,0需要注意啊~
1093 骆驼和香蕉(2019.1.8)
题目描述
一只骆驼每次最多负重 K K K只香蕉,而它每走1公里要吃掉1只香蕉,不吃完不肯走。现在这只骆驼要去到N公里以外的地方,如果 N > K N > K N>K,那么即使骆驼装满了香蕉,也无法1次走到目的地,不过骆驼可以在中途设置一些补给点,先把一些香蕉运过去,下次经过时可以在这些地方进行补给。这样一来便能走到距离 > K > K >K的地方。现在给出 N N N和 K K K,问骆驼走到目的地最少需要消耗多少香蕉。
输入
2个整数N K,中间用空格分隔。(1 <= N, K <= 10000,N <= 5 * K)
输出
输出最少需要消耗多少根香蕉。
输入样例
1000 500
输出样例
3837
时限
1s
解析
吐槽
(为什么过这么久的题了,我还要吐槽???因为居然考到原题了)
小数那个进制还是很让人讨厌的啊~
分析
当确定了补给站的时候,补给点就会被分成若干段~且次数是(1,3,5,7……)无需证明的吧 ~
因此如果我们想恰好送完补给,并走到终点,两个点的距离就应为k/cnt[i] 指这一段被经过的次数~(似乎成了简单贪心)
代码
#include<bits/stdc++.h>
using namespace std;
double n,k,s,i;
long long j;
int main()
{
scanf("%lf%lf",&n,&k);
if(n<=k)
{
j=n;
if(j!=n)++j;
printf("%lld",j);
return 0;
}
i=1.0;
while(n)
{
//cout<<"a="<<k/i<<endl;
s+=min((k/i),n)*i;//cout<<"s="<<s<<endl;
n-=min((k/i),n);//cout<<"n="<<n<<endl;
i+=2;//cout<<"i="<<i<<endl;
//cout<<"----------------\n";
}
j=s;
if(s>j)++j;
printf("%lld",j);
return 0;
}
1509 加长棒
题目描述
现在有三根木棒,他们的长度分别是 a , b , c a,b,c a,b,c厘米。你可以对他们进行加长(不同的木棒可以增加不同的长度),他们总的加长长度不能超过 L L L厘米。你也可以不对他们进行加长。
现在请你计算一下有多少种加长的方式使得他们能构成合法的三角形(面积非0)。
输入
单组测试数据。
共一行,包含4 个整数
a
,
b
,
c
,
L
(
1
≤
a
,
b
,
c
≤
3
∗
1
0
5
,
0
≤
L
≤
3
∗
1
0
5
)
a,b,c,L (1≤a,b,c≤3*10^5, 0≤L≤3*10^5)
a,b,c,L(1≤a,b,c≤3∗105,0≤L≤3∗105)。
输出
输出答案占一行。
输入样例
1 1 1 2
输出样例
4
解析
吐槽
(同样是考到了)
因为题解的解析和代码不对应,所以要我补写一个……
分析
先挡板法,总条数为
C
(
L
+
4
−
1
,
4
−
1
)
C(L+4-1,4-1)
C(L+4−1,4−1)
你看,要满足三角形的条件是什么??
任意两边之和大于第三边,对吧~
假设
a
a
a为最大值~
所以就有
b
+
c
≥
a
b+c≥a
b+c≥a
因为我们所要加的长度,不能超过
L
L
L
所以我们给
a
a
a加上
x
x
x,给
b
b
b加上
y
y
y,给
c
c
c加上
z
z
z;
所以又有
x
+
y
+
z
≤
L
x+y+z≤L
x+y+z≤L
因为
x
,
y
,
z
都
不
能
≤
0
x,y,z都不能≤0
x,y,z都不能≤0
所以当下面的情况肯定不满足
b
+
c
≤
b
+
c
+
y
+
z
b+c≤b+c+y+z
b+c≤b+c+y+z
(为了方便压行
b
+
c
+
y
+
z
b+c+y+z
b+c+y+z又等于
a
+
b
+
c
+
L
−
a
−
x
a+b+c+L-a-x
a+b+c+L−a−x,下面就这么写)
现在我们还可以发现
b
+
y
+
c
+
z
>
a
+
x
b+y+c+z>a+x
b+y+c+z>a+x
显然
所以这个只需
b
+
c
>
a
+
x
b+c>a+x
b+c>a+x
然后如果
b
+
c
≤
a
+
x
的
时
候
b+c≤a+x的时候
b+c≤a+x的时候就又是不满足的,对于上述两种不同的情况取个
m
i
n
min
min好在前者都是
b
+
c
b+c
b+c
隔板法求出不能是
C
(
m
i
n
(
.
.
.
.
)
−
b
−
c
+
2
,
2
)
C(min(....)-b-c+2,2)
C(min(....)−b−c+2,2)
注意
l
o
n
g
l
o
n
g
long long
longlong,
C
(
L
+
4
−
1
,
4
−
1
)
C(L+4-1,4-1)
C(L+4−1,4−1)时
50000
50000
50000直接起飞~
代码
#include<bits/stdc++.h>
using namespace std;
long long a,b,c,L,ans,n,m;
void work(long long _a,long long _b,long long _c, long long L)
{
int i;
for(i=-1;++i<=L;)
{
m=min(n-_a-i,i+_a);
if(_b+_c<=m)ans-=(m-_b-_c+2)*(m-_b-_c+1)/2;
}
}
int main()
{
scanf("%lld%lld%lld%lld",&a,&b,&c,&L);
n=a+b+c+L;
ans=(L+3)*(L+2)*(L+1)/6;
work(a,b,c,L);work(b,a,c,L);work(c,a,b,L);
printf("%lld",ans);
return 0;
}
5级题
1053 最大M子段和 V2(2018.12.29)
题目描述
N
N
N个整数组成的序列
a
[
1
]
,
a
[
2
]
,
a
[
3
]
,
…
,
a
[
n
]
a[1],a[2],a[3],…,a[n]
a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这
M
M
M个子段的和是最大的。如果
M
>
=
N
M >= N
M>=N个数中正数的个数,那么输出所有正数的和。
例如:-2 11 -4 13 -5 6 -2,分为2段,11 -4 13一段,6一段,和为26。
输入
第1行:2个数
N
N
N和
M
M
M,中间用空格分隔。
N
N
N为整数的个数,M为划分为多少段。
(
2
<
=
N
,
M
<
=
50000
)
(2 <= N , M <= 50000)
(2<=N,M<=50000)
第
2
−
N
+
1
2 - N+1
2−N+1行:
N
N
N个整数
(
−
1
0
9
<
=
a
[
i
]
<
=
1
0
9
)
(-10^9 <= a[i] <= 10^9)
(−109<=a[i]<=109)
输出
输出这个最大和
输入样例
7 2
-2
11
-4
13
-5
6
-2
输出样例
26
时限
2s
解析
牛掰的题目,把我难住了~
我忘了~
代码
#include<bits/stdc++.h>
using namespace std;
long long n,m,l[500003],r[500003],a[500003];
typedef pair<long long,long long> ss;
set<ss> v;
void del(long long a)
{
long long L=l[a],R=r[a];
if(L) r[L]=R;
if(R) l[R]=L;
}
int main()
{
long long N;
scanf("%lld%lld",&N,&m);
long long ans=0,sum=0,ds=0;
for(long long i=1;i<=N;i++)
{
long long x;
scanf("%lld",&x);
if((sum>0&&x<0)||(sum<0&&x>0))
{
a[++n]=sum;
ds+=sum>0;
v.insert(ss(abs(sum),n));
sum=0;
}
sum+=x;
if(x>=0) ans+=x;
}
a[++n]=sum;
ds+=sum>0;
v.insert(make_pair(abs(sum),n));
for(long long i=1;i<=n;i++) l[i]=i-1,r[i]=(i<n)?i+1:0;
while(ds>m)
{
long long cur=v.begin()->second;
v.erase(v.begin());
if((a[cur]<0&&(!l[cur]||!r[cur]))||!a[cur])continue;
v.erase(ss(abs(a[l[cur]]),l[cur]));
v.erase(ss(abs(a[r[cur]]),r[cur]));
ans-=abs(a[cur]);
a[cur]+=a[l[cur]]+a[r[cur]];
del(l[cur]); del(r[cur]);
v.insert(ss(abs(a[cur]),cur));
--ds;
}
printf("%lld\n",ans);
return 0;
}