一、游记
Day 0
在家里复习,毫无规划意识,一会复习下贪心,一会复习下二分,后来发现其他人在复习LCA,于是赶紧放下手中的贪心题,去看一下Tarjan吧。。。
于是瞎搞到十点半,发现自己什么都没有复习到,忧心忡忡地去睡觉了。
Day 1
六点半起床,迷迷糊糊的上了车。
到了场地后,和大家在外面一起拍了照,小黄鸭的邪魅一笑,开了一会玩笑,就进场了。
发现键盘特别不好用,换行贼大,退格贼小,适应了一下,就开始了。
T1,不会,于是手推了一下,发现是签到题。
T2,不会,发现可以用两个优先队列来判断P上和P下,但是写不来小根堆。。。
T3,这不是我最爱的模拟吗???暴力模拟,30分到手。
T4,告诉我们不要乱看题,我把方向看成了4个方向都可以走,于是只会打DFS了。。。
还有一个多小时,发现T3过不了大样例,于是又去改,后来过了大样例,发现时间复杂度不对,于是尝试标记每个点是否会对答案产生影响,于是在输入的时候对ans数组初始化,发现没用,但如果每次操作都改变ans好像有用???
然后神奇的发现,又改成暴力了。。。
二、题解
T1
solution
二进制拆分。
#include<bits/stdc++.h>
using namespace std;
int n;
int sum[100005];
int main(){
scanf("%d",&n);
int tot=0;
for(int i=2;i<=n;i*=2){
sum[++tot]=i;
}
if(n%2==0){
for(int i=tot;i>=1;i--){
if(sum[i]<=n){
n-=sum[i];
printf("%d ",sum[i]);
}
if(n==0){
return 0;
}
}
return 0;
}
printf("-1");
return 0;
}
T2
solution
因为数据规定,每个数不大于600,所以可以想到用桶来储存,如果i出现,就将tot[i]+1,每次输出时从后往前遍历桶,如果到达了要求的人数,就输出当前的桶。
#include<bits/stdc++.h>
using namespace std;
int a[1000005];
int tot[605]={
};
int n,m;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
tot[a[i]]++;
int ans=0;
int p=max(1,i*m/100);
for(int j=600;j>=1;j--){
ans+=tot[j];
if(ans>=p){
printf("%d ",j);
break;
}
}
}
return 0;
}
T3
solution
不会。。。
T4
solution
dp[i][j][0]表示其是由其左侧点或上侧点更新
dp[i][j][1]表示其是由其左侧点或下册点更新
因为点(n,m)只能由其上侧或左侧的点更新,所以答案即为dp[n][m][0]
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll dp[1005][1005][5];
ll a[1005][1005];
ll n,m;
ll Max(ll a,ll b){
return a>b?a:b;
}
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%lld",&a[i][j]);
}
}
memset(dp,-0x3f,sizeof dp);
dp[1][0][0]=0;
for(int j=1;j<=m;j++){
for(int i=1;i<=n;i++){
dp[i][j][0]=Max(dp[i-1][j][0],Max(dp[i][j-1][0],dp[i][j-1][1]))+a[i][j];
}
for(int i=n;i>=1;i--){
dp[i][j][1]=Max(dp[i+1][j][1],Max(dp[i][j-1][0],dp[i][j-1][1]))+a[i][j];
}
}
printf("%lld",dp[n][m][0]);
return 0;
}