这次比赛,不难,很水(即使没人AK)。Rank.1,370分。
还没开始提交,我就已经把全部题都做完了,但是考虑的不够周全,丢了一些分。
估分:100+100+100+100=400
实际:100+100+90+80
T1
平台
Description
Alice要搭建平台,平台不能漂在空气中,必须要有两根柱子支撑,具体地说,每个平台的两端必须由一根柱子支撑,柱子的另一端在地板或另一个平台上。
给你平台的放置位置(如下左图所示),每个平台的位置由它的高度(离地面的垂直距离)和水平方向两个端点的坐标决定,每根柱子必须安放在离端点0.5个单位的位置。
编程计算所需柱子总长是多少。
Solution
直接暴力即可。O(n^2)
估分:100
实际:100
#include<cstdio>
using namespace std;
int n,y[101],x[101],x2[101],ans;
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&y[i],&x[i],&x2[i]);
for(int i=1;i<=n;i++) {
int maxn=0,MAX=0;
for(int j=1;j<=n;j++)
if(y[j]<y[i]) {
if(x[j]<=x[i]&&x2[j]>x[i]&&y[j]>maxn)
maxn=y[j];
if(x[j]<x2[i]&&x2[j]>=x2[i]&&y[j]>MAX)
MAX=y[j];
}
ans+=y[i]-maxn+y[i]-MAX;
}
printf("%d",ans);
}
T2
单足跳
Description
游戏在一行N个方块中进行,编号为1到N,一开始Alice在方块1中,第一次只能跳到方块2中,接下来每一次跳跃必须满足以下两个限制:
(1) 如果是向前跳(即跳到比现在编号大的方块),跳跃距离必须比上一次要大1;
(2) 如果是向后跳(即跳到比现在编号小的方块),跳跃距离必须跟上一次一样。
例如,第一次跳跃后,Alice可以跳回1也可以跳到4。
每进入一个方块,Alice必须支付一定的费用,Alice的目标花最少的钱从方块1跳到方块N。编程计算最小的花费。
Solution
dp。设f[i][j]表示跳i的距离,到j这个格子的最小分数。
很容易得到方程:
f[i][j]=min(f[i][j],f[i-1][j-i]+a[j]);
f[i][j]=min(f[i][j],f[i][i+j]+a[j]);
估分:100
实际:100
#include<cstdio>
#include<cstring>
using namespace std;
int n,a[1001],f[1001][1001],ans=0x3f3f3f3f;
int min(int x,int y) {
if(x<y)return x;
return y;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(f,0x3f3f3f3f,sizeof(f));
f[0][1]=0;
for(int i=1;i<=n;i++)
for(int j=n;j;j--) {//注意j要倒着
if(j+i<=n)f[i][j]=min(f[i][j],f[i][j+i]+a[j]);
if(j-i>0)f[i][j]=min(f[i][j],f[i-1][j-i]+a[j]);
if(j==n)ans=min(ans,f[i][j]);
}
printf("%d",ans);
}
T3
生日聚餐
Description
Alice在餐馆里当服务员,今天是她生日,她请求厨师帮她准备生日晚餐,晚餐由N种原料做成,每道菜所需每种原料的数量是一样的。
厨房里有一些原料,但不够,Alice还需要从旁边的超市中购买一些回来。超市里什么原料都有,每种原料都分大包装和小包装。Alice有M元钱,她想利用这M元钱购买原料使得能做出最多的菜。
Solution
先考虑dp,设f[i][j]表示第i种原料用了j块钱的最大数量。转移就用类似背包的东西,不在赘述。
然后,考虑用二分。判断一个mid是否正确,枚举每一个原料,然后二分查找f[i][j]等于mid的一个最小j。
最后把所有j相加,判断是否超出m,如果超出m,则是不合法。
估分:100
实际:90(比赛时是另一种二分,空间卡住了)
#include<cstdio>
using namespace std;
int n,m,a[101],b[101],f[101][100001],l=0,r=100000,ans;
int max(int c,int d) {
if(c>d)return c;
return d;
}
inline bool check(int k) {
int t=0;
for(register int i=1;i<=n;i++) {
if(f[i][m]<k)return 0;
int left=0,right=m,ret=0;
while(left<=right) {
int middle=(left+right)>>1;
if(f[i][middle]>=k)right=middle-1,ret=middle;
else left=middle+1;
}
t+=ret;
}
if(t<=m)return 1;
return 0;
}
int read() {
int s=0;
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')
s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
return s;
}
int main() {
n=read();m=read();
for(register int i=1;i<=n;i++) {
a[i]=read();
b[i]=read();
int x,y,u,v;
x=read();y=read();
u=read();v=read();
for(register int j=1;j<=m;j++) {
if(j-y>=0)f[i][j]=max(f[i][j],f[i][j-y]+x);
if(j-v>=0)f[i][j]=max(f[i][j],f[i][j-v]+u);
}
for(register int j=1;j<=m;j++)
f[i][j]=(f[i][j]+b[i])/a[i];
}
while(l<=r) {
int mid=(l+r)>>1;
if(check(mid))l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d",ans);
}
T4
数学题
Description
当Alice在浏览数学书时,看到一个等式A=S,奇怪的是A和S并不相等。Alice发现可以通过在A中添加加号“+”从而使得等式成立。
编程计算最少需要插入多少加号使得等式成立。允许每个数有多个前导0。
Solution
So easy。但是有两个点把我给卡了。也是考虑dp(dp题真多)。
设f[i][j]表示到第i位和为j的最小加号数。
转移很显然,不赘述。
估分:100
实际:80(被卡了两个点,没有过滤掉0)
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
int s=0,n,a[1001],f[1001][5001];
inline void init() {
int len=0;
char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')a[++len]=c^48,c=getchar();
n=len;
c=getchar();
while(c>='0'&&c<='9')s=(s<<1)+(s<<3)+(c^48),c=getchar();
for(int i=0;i<=n;i++)
for(int j=0;j<=s;j++)
f[i][j]=1005;
}
inline int min(int u,int v) {
if(u<v)return u;
return v;
}
inline void solve() {
f[0][0]=0;
for(register int i=0; i<n; i++) {
int l=i+1;
while(a[l]==0&&l<=n)++l;//一定要把开头0过滤掉,否则有两个点跑步过去
if(l>n)l=n;
for(int j=0; j<=s; j++) {
if(f[i][j]==1005)continue;
int x=0;
for(register int k=l; k<=n; k++) {
x=(x<<1)+(x<<3)+a[k];
if(x+j>s)break;
f[k][j+x]=min(f[k][j+x],f[i][j]+(k!=n));
}
}
}
printf("%d",f[n][s]);
}
signed int main() {
init();
solve();
}
额……NOI Online连暴力都码错了,一共只有10分,作为初一的蒟蒻,还要努力。
反思:
1、一定要考虑的周全一点(包括时间,空间,数据大小等……)。
下次加油!