前言
好吧,由于赛时本人还是一个蒟蒻,(虽然现在也是),导致一直没有做完后面两题。。。
先说下赛时分数吧。。。
前两题日常水过,后两题日常放弃,其实第三题有想着做但是没时间了
第一题 | 第二题 | 第三题 | 第四题 |
---|---|---|---|
Accepted | Accepted | WA | WA |
。。。
解题报告
第一题 成绩
链接
https://www.luogu.org/recordnew/lists?uid=52915&pid=3954
大意
输入 a,b,c a , b , c 计算 a∗0.2+b∗0.3+c∗0.5 a ∗ 0.2 + b ∗ 0.3 + c ∗ 0.5
思路
模拟,由于
C++
C
+
+
的强制转换很骚,所以就要注意一下
时间复杂度和空间复杂度都是
O(1)
O
(
1
)
代码
#include<cstdio>
#define sr c=getchar()
#define input read()
#define pd (c<'0'||c>'9')
using namespace std;
int read()//楼楼超丑的代码,莫介意
{
int d=1,f=0;char c;
while (sr,pd) if (c=='-') d=-1;f=f*10+c-48;
while (sr,!pd) f=f*10+c-48;
return d*f;
}
int main()
{
int x,y,z;
x=input;y=input;z=input;
int a,b,c;
a=x*20;b=y*30;c=z*50;
int ans=(a+b+c)/100;
printf("%d",ans);//防止强制转换出现的bug
}
第二题 图书管理员
链接
https://www.luogu.org/recordnew/lists?uid=52915&pid=3955
大意
忘了(真的忘了。。。,真的不是我懒)
思路
重点在%,模拟,本人代码奇丑无比,奇low无比(当时是真的蒟蒻)
时间复杂度:
O(nlogn+m(nleni+∑len))
O
(
n
l
o
g
n
+
m
(
n
l
e
n
i
+
∑
l
e
n
)
)
空间复杂度:
O(2n+m)
O
(
2
n
+
m
)
(丑到怀疑人生)
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define sr c=getchar()
#define input read()
#define pd (c<'0'||c>'9')
using namespace std;
int n,m;
int book[1011]; int z[1011];
int numb[111];//一群丑数组
int len;
bool ok;
bool cmp(int x,int y)//一个丑排序
{
return x<y;
}
bool pds(int x)//一个丑判断
{
int j=0;
while (x>0)
{
j++;
z[j]=x%10;
x/=10;
//printf("%d",z[j]);
}
int i=0;
if (len>j) return false;
for (int k=len;k>0;k--)
{
i++;
if (z[i]!=numb[k]) return false;
}
return true;
}
int read()//一个丑输入流
{
char c;int d=1,f=0;
while (sr,pd) if (c=='-') d=-1;f=f*10+c-48;
while (sr,!pd) f=f*10+c-48;
return d*f;
}
int main()
{
n=input;m=input;
for (int i=1;i<=n;i++)
book[i]=input;//一个丑输入
sort(book+1,book+1+n,cmp);//丑排序
for (int i=1;i<=m;i++)
{
len=input;char lc;//memset(numb,0,sizeof(numb));
for (int j=1;j<=len;j++)
{
lc=getchar();
numb[j]=lc-48;
}ok=false;//丑输入
for (int j=1;j<=n;j++)
{
if (pds(book[j])) {ok=true;printf("%d\n",book[j]);break;}//丑查找
}
if (!ok) printf("-1\n");//丑判断
}
return 0;//丑return
}
终于丑完了
第三题 棋盘
链接
https://www.luogu.org/recordnew/lists?uid=52915&pid=3956
大意
有一个
n×n
n
×
n
的棋盘,有
m
m
个色块是有色的。
有色的色块间有两种规则
- 若两个格子颜色相同,则这两个格子之间行走的代价为0
- 若两个各自颜色不同,则这两个格子之间行走的代价为1
有色和无色或无色和有色间有一种规则
可以施展膜拜大法(魔法),将无色的方格变成有色的方格的颜色,代价为2
现求从左上角走到右下角的最小花费,若不能到达,输出-1
思路
暴搜会超时,解法很多,这里列举一下
- bfs
- dfs+剪枝
- dp
最短路(这个很复杂,洛谷上有详细介绍,这里主要讲dfs)
其实dfs很容易理解,就是开一个表示是否有使用魔法,也可以直接在 dfs d f s 里面加一个 flag f l a g 来判断,效果是一样的
值得注意的是,走过的路还能再走,所以不用判断是否已经走过,而是保存最优解
时间复杂度: O(n2) O ( n 2 ) (应该是吧)
空间复杂度: O(3n2) O ( 3 n 2 ) (若使用flag作为一个参数,空间复杂度可一将为 O(2n2) O ( 2 n 2 ) )代码
终于不是奇丑无比了,当然在 dalao d a l a o 面前永远是丑的。。。
// luogu-judger-enable-o2 #include<cstdio> #include<algorithm> #include<cstring> #define LL long long #define r(i,a,b) for(int i=a;i<=b;i++) #define check(x,y) (x>=1&&x<=n&&y>=1&&y<=n)//判断是否在期盼内 using namespace std;int n,m,ans=536870912; const short dx[4]={-1,0,1,0}; const short dy[4]={0,1,0,-1};//四个方向 bool use[101][101];int color[101][101],f[101][101];//use为是否使用魔法,color表示此点的颜色,f表示最优解 LL read()//输入流 { char c;int f=0,d=1; while((c=getchar())<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48; while((c=getchar())>=48&&c<=57)f=(f<<3)+(f<<1)+c-48; return d*f; } void dfs(int x,int y,int now) { if(!check(x,y)||now>=f[x][y]) return;//超出范围或者不是最优则退出 if(x==n&&y==n) {ans=min(now,ans);return;}f[x][y]=now;//保存 r(i,0,3) { int qx=x+dx[i],qy=y+dy[i];//获取位置 if(check(qx,qy))//判断 { if(use[x][y]&&!color[qx][qy]) continue;//若已经使用魔法但是目标格子是空的那么直接返回 if(color[qx][qy]) if(color[x][y]==color[qx][qy]) dfs(qx,qy,now);//颜色相同 else dfs(qx,qy,now+1);//不相同 else if(!use[x][y]&&!color[qx][qy])//使用魔法 { use[qx][qy]=true; color[qx][qy]=color[x][y];//标记已经使用并且改变颜色 dfs(qx,qy,now+2); use[qx][qy]=false; color[qx][qy]=0;//回溯 } } } } int main() { memset(f,127/3,sizeof(f)); n=read();m=read(); r(i,1,m) color[read()][read()]=read()+1;//输入,为了更好区分,所以每个格子都+1,这样区分开了红色和白色的格子 dfs(1,1,0);//搜索 if(ans==536870912) puts("-1");else printf("%d",ans);//输出 }
第四题 跳房子
链接
https://www.luogu.org/problemnew/show/P3957
大意
在一根数轴上有 n n 个点,给出它们距离原点的距离以及它们的价值,现在有一个机器人,他每次可以向右边走个单位长度,但是为了的得到一定的价值 k k ,需要改进这个机器人。已知花费点金币可以使它能跳的距离变成
max(1,d−g)...g+d m a x ( 1 , d − g ) . . . g + d 之间,问至少需要多少金币可以拿到 k k 点价值思路
首先可以想到若使用枚金币可以,那么使用 g+1 g + 1 枚也必然可以,所以这就满足二分的条件
至于如何确定二分的答案是否正确呢?需要用到动态规划,先给出方程
dp[i]=max(dp[i],dp[j]+a[i]) d p [ i ] = m a x ( d p [ i ] , d p [ j ] + a [ i ] )
当然,这个转移是一定要满足可以跳到这个位置的情况下的,时间复杂度为 O(logMaxn×n2) O ( l o g M a x n × n 2 ) 会超时
所以要用到单调队列优化
先简单提一下单调队列,例如现在此队列为
9 8 7 6 5 2 1
现在我要插入7,则单调队列变成
9 8 7 而不是 9 8 7 6 5 2 1 7
时间复杂度: O(logMaxn×n) O ( l o g M a x n × n )
空间复杂度: O(3n) O ( 3 n )代码(STL)
#include<cstdio> #include<algorithm> #include<deque> #define LL long long #define r(i,a,b) for(int i=a;i<=b;i++) #define N 500001 using namespace std;LL n,d,k,l,r,mid,far[N],num[N],ans=-1,dp[N]; LL read() { char c;int f=0,d=1; while((c=getchar())<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48; while((c=getchar())>=48&&c<=57)f=(f<<3)+(f<<1)+c-48; return d*f; } bool check(int money) { deque<int>q; far[0]=0; fill(dp,dp+n,0);//初始化 int high=money+d,low=max(d-money,(LL)1);//记得末尾是1 int j=0; r(i,1,n) { while(far[i]-far[j]>=low) { while(!q.empty()&&dp[j]>=dp[q.back()]) q.pop_back();//弹出去 q.push_back(j++);//放进来 } while(!q.empty()&&far[i]-far[q.front()]>high) q.pop_front();//弹出去 if(q.empty())dp[i]=-9999999999;//空了 else dp[i]=dp[q.front()]+num[i];//没空 if(dp[i]>=k) return 1;//判断 } return 0; } int main() { n=read();d=read();k=read(); r(i,1,n) far[i]=read(),num[i]=read(); int l=1,r=N<<5;//范围 while(l<=r) { mid=(l+r)>>1; if(check(mid)) ans=mid,r=mid-1;else l=mid+1;//二分 } printf("%lld",ans); }
代码(单调队列)
#include<cstdio> #include<algorithm> #define LL long long #define r(i,a,b) for(int i=a;i<=b;i++) #define N 500001 using namespace std;LL n,d,k,l,r,mid,far[N],num[N],ans=-1,dp[N]; LL read() { char c;int f=0,d=1; while((c=getchar())<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48; while((c=getchar())>=48&&c<=57)f=(f<<3)+(f<<1)+c-48; return d*f; } bool check(int money)//方法和上面是一样的 { int q[N]={0}; fill(dp,dp+n,-1);dp[0]=0; int high=money+d,low=max(d-money,(LL)1); int j=0,head=1,tail=0; r(i,1,n) { while(far[i]-far[j]>=low) { while(head<=tail&&dp[j]>=dp[q[tail]]) tail--; q[++tail]=j++; } while(head<=tail&&far[i]-far[q[head]]>high) head++; if(head<=tail)dp[i]=dp[q[head]]+num[i];else dp[i]=-9999999999; if(dp[i]>=k) return 1; } return 0; } int main() { n=read();d=read();k=read(); r(i,1,n) far[i]=read(),num[i]=read(); int l=1,r=far[n]; while(l<=r) { mid=(l+r)>>1; if(check(mid)) ans=mid,r=mid-1;else l=mid+1;//二分 } printf("%lld",ans);//输出 }