T1 斯卡布罗集市 50/100
共t条街对于每一条街上有n个店铺(n可能不相同),每次只能选两端第一个营业的店铺采购,采购第i个店铺会获得幸福度ai,采购完后,这个店铺和它相邻的店铺便会关门,问最大幸福度?
考场想了一下dp,一开始想一维但发现不好处理,二维参数也没有想出来,于是便开始了我的暴力瞎搞之旅,我随手写了几个例子发现对于n为奇数无论怎么采购,幸福度是固定的为a1, a3 , a5........an于是便可以直接累加,然而对于n为偶数又怎么去处理呢?我也随手写了n为6,8,10的数据发现其实就只有三种情况。
第一种情况:选编号为奇数的
第二种情况:选编号为偶数的
第三种情况:选两头,再考虑中间,举个栗子
3 8 20 8 15 12 12 25
对于这组数据我们选完a1 a8后再去进行选择a3 a5我们把选了用o表示没选的用x表示就是
o x o x o x x
貌似一定又两个连在一起的不选,但考场我也没去证明就直接用了,枚举这两个不选的位置就行,跑完样例发现过了便直接提交了,结果a了,自己也很懵逼。
代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
int t,n;
long long f[310][1510],ans,tmp1,tmp2;
int main(){
t=read();
for(int i=1;i<=t;++i){
n=read();
for(int j=1;j<=n;++j){
f[i][j]=read();
}
if(n&1){
for(int j=1;j<=n;++j){
if(j&1) ans+=f[i][j];
}
}
else{
tmp2=0;
for(int j=0;j<=n;++j){
tmp1=0;
for(int k=1;k<j;k+=2){
tmp1+=f[i][k];
}
for(int k=j+2;k<=n;k+=2){
tmp1+=f[i][k];
}
tmp2=max(tmp1,tmp2);
}
ans+=tmp2;
}
}
printf("%lld",ans);
return 0;
}
貌似可以hack掉,可能数据比较水就a了,再放一个大佬写的前缀和表达
#include<algorithm>
#include<cstdio>
#define ll long long
#define MX 10001
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
int T,n;
ll ans=0;
int a[MX];
ll suml[MX],sumr[MX];
int main(){
T=read();
while(T--){
n=read();
sumr[n+1]=0;
for(int i=1;i<=n;++i){
a[i]=read();
if(i&1){
suml[i]=suml[i-1]+a[i];
}else{
suml[i]=suml[i-1];
}
}
if(n&1){
ans+=suml[n];
}else{
for(int i=n;i>=1;--i){
if(i&1){
sumr[i]=sumr[i+1];
}else{
sumr[i]=sumr[i+1]+a[i];
}
}
ll sum=0;
for(int i=1;i<=n;++i){
sum=max(sum,suml[i-1]+sumr[i+1]);
}
ans+=sum;
}
}
printf("%lld",ans);
return 0;
}
T2 战争 100/100 (个人题库里有)
纯数论
30分:纯暴力,直接模拟判断t秒后,判断hp是否小于0
60分: atk>=h,就是一炮一个,那么军队会在min(n,t)秒之后停止攻击,那么总伤害就是a[n+(n-1)
+(n-2)+........(n-min(n,t)+1)
等差数列求和d=a(2n-min(n,t)+1)*(min(n,t))/2;
如果d>=hp Yes,否则No;
100分
考虑前t秒军队伤害总和,然后和hp比较大小
先算几下打死一个士兵,设m下
那么前m秒伤害为nma
m+1到2m秒伤害为m(n-1)a
以此类推
如果没有支援,那么可以到m(1)a
那么我们考虑支援,将其分开算
对于前面的时间段,每个时间段经历m秒,设k表示有几个经历了m秒的时间段,那么
k=min(n,t/m)
设k个时间段的伤害为d=ma(n)+ma(n-1)......ma(n-k+1)
等差数列求和d=(ma(2n-k+1)k)/2
如果(t/m)<n && t mod m !=0则还有一个时间段,经历了t mod m秒
伤害为(t mod m)(a)(n-k)加入中
最后判断d>=hp如果是输出Yes,否则输出No
代码
#include<bits/stdc++.h>
using namespace std;
int T,flag;
long long hp,atk,n,h,a,t,atck,tmp,k;
int main(){
scanf("%d",&T);
while(T){
scanf("%lld %lld %lld %lld %lld %lld",&hp,&atk,&n,&h,&a,&t);
tmp=h/atk;
if(h%atk!=0)tmp+=1;
k=min(n,t/tmp);
atck=tmp*a*(2*n-k+1)*k/2;
if(t/tmp<n&&t%tmp!=0){
atck+=t%tmp*a*(n-k);
}
if(atck>=hp){
printf("Yes
");
}
else printf("No
");
T--;
}
return 0;
}
T3 鸽鸽行动 30/30 (之前那个大佬出的)
就是把n* m的矩阵划分成四个部分从四个部分中选4个k* k的小矩阵,每个点有权值要求权值和最大。
考场先求前缀和,暴力枚举‘+’切割点再枚举k* k的矩阵,就只有30分,距离正解还是比较近了。
正解是先枚举矩阵预处理出来再去枚举点便可去掉两重循环。。。
代码
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<iostream>
#define MX 1305
using namespace std;
int n,m,k;
int h;
int sum[MX][MX];
int ans;
int a[MX][MX],b[MX][MX],c[MX][MX],d[MX][MX];
int main(){
//freopen("ggaction.in","r",stdin);
//freopen("ggaction.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
scanf("%d",&h);
sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+h;
}
}
for(int i=n;i>=k;--i){
for(int j=m;j>=k;--j){
sum[i][j]-=sum[i-k][j]+sum[i][j-k]-sum[i-k][j-k];
}
}
for(int i=k;i<=n;++i){
for(int j=k;j<=m;++j){
a[i][j]=max(sum[i][j],max(a[i-1][j],a[i][j-1]));
}
}
for(int i=k;i<=n;++i){
for(int j=m-k+1;j>=1;--j){
b[i][j]=max(sum[i][j+k-1],max(b[i-1][j],b[i][j+1]));
}
}
for(int i=n-k+1;i>=1;--i){
for(int j=k;j<=m;++j){
c[i][j]=max(sum[i+k-1][j],max(c[i+1][j],c[i][j-1]));
}
}
for(int i=n-k+1;i>=1;--i){
for(int j=m-k+1;j>=1;--j){
d[i][j]=max(sum[i+k-1][j+k-1],max(d[i+1][j],d[i][j+1]));
}
}
for(int i=k;i<=n-k;++i){
for(int j=k;j<=m-k;++j){
ans=max(ans,a[i][j]+b[i][j+1]+c[i+1][j]+d[i+1][j+1]);
}
}
printf("%d
",ans);
return 0;
}