ZJ第六套总结
其实徐妈口中的所谓的ZJ十套就是《全国青少年信息学竞赛培训教材复赛》中的十套膜你题。今天先应徐妈之要求把今天的总结了,以后再补之前的。
分组(group)
洛谷题目:P1109 学生分组
大水题,直接放代码。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
inline int read()
{
char ch=getchar();
int f=1,x=0;
while (ch<'0' || ch>'9')
{
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
int n,l,r,a,sum,ans1,ans2,ans,s[55];
int main()
{
freopen("group.in","r",stdin);
freopen("group.out","w",stdout);
n=read();
for (int i=1;i<=n;i++) s[i]=read();
l=read(); r=read();
for (int i=1;i<=n;i++)
{
sum+=s[i];
if (s[i]>=l && s[i]<=r) continue;
if (s[i]>r) ans1+=s[i]-r;
if (s[i]<l) ans2+=l-s[i];
}
ans=max(ans1,ans2);
if (sum>n*r || sum<n*l) printf("-1");
else printf("%d",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
阶乘(num)
洛谷上并没有这道题。
找下规律,发现末尾都是偶数,而且如果没有5的话直接按照个位来乘就行了。但是遇到5、15之类的就非常恶心了,所以对于这些肯定要分类讨论。
可以知道,最后的0的来源肯定是2和5,显然,5的个数小于2的个数,所以我们考虑5。
首先假设没有5存在。即便这样 $ O(n) $ 的时间复杂度还是不现实的。由于只和最后一位有关,所以我们可以将其10个分为一组运算,这样最多算2009次就行了。
最后总的阶乘和 $ \div 10^{x} $ 就是答案,我们不考虑5的话只需要最后 $ \div 2^{x} $ 就行了(x是5的个数也是0的个数)。所以最后有多少个5我们 $ \div 2 $ 多少个2就行了。可以发现 $ \div 2 $ 后的对应关系
- 2—>6
- 4—>2
- 6—>8
- 8—>4
所以不断除5,并且把答案不断除2也就是变成对应关系就行了,其他的就按10分组直接算。显然要用高精。
然后我在考场上很开心的写完了TLE代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
inline int read()
{
char ch=getchar();
int f=1,x=0;
while (ch<'0' || ch>'9')
{
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
struct bigint{
int s[5005],len;
bigint(){memset(s,0,sizeof(s));len=0;}
} n;
int t,a,b,c,ans;
inline bigint division(bigint x,int y)
{
bigint z;
int sum=0,len=x.len;
for (int i=len;i;i--)
{
sum=sum*10+x.s[i];
z.s[i]=sum/y;
sum%=y;
}
while (!z.s[len]) len--;
z.len=len;
return z;
}
inline void clear(bigint &x)
{
for (int i=1;i<=x.len;i++)
x.s[i]=0;
x.len=0;
}
int main()
{
freopen("num.in","r",stdin);
freopen("num.out","w",stdout);
t=read();
while (t--)
{
clear(n); ans=1;
char ch[2018];
scanf("%s",ch);
for (int i=strlen(ch)-1;i>=0;i--) n.s[++n.len]=ch[i]-'0';
if (n.len==1 && n.s[1]==1)
{
printf("1\n");
continue;
}
while (n.len>=1)
{
for (int i=1;i<=n.s[1];i++)
{
if (i==5) continue;
ans=(ans*i)%10;
}
n=division(n,5);
int sum=n.s[1]+n.s[2]*10;
if (sum%4==0) ans=(ans*6)%10;
else if (sum%4==1) ans=(ans*8)%10;
else if (sum%4==2) ans=(ans*4)%10;
else if (sum%4==3) ans=(ans*2)%10;
}
printf("%d\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}
然后很开心的得了50.
考后看书才发现那个每一位上都循环乘一遍太浪费时间了,树上直接预处理了一个数组:
int val[10]={1,1,2,6,4,4,4,8,4,6};
时间复杂度变成了原来的 $ \dfrac {1}{10} $ ,但还是50分。我想是不是高精度初始化和clear太浪费了,就把它删了,毕竟都是重新赋值。然后就有70分了。最后把函数直接写在了main
函数里就A了。这TM卡常是有多严重啊
附上AC代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
inline int read()
{
char ch=getchar();
int f=1,x=0;
while (ch<'0' || ch>'9')
{
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
struct bigint{
int s[3005],len;
} n;
int t,a,b,c,ans,val[10]={1,1,2,6,4,4,4,8,4,6};
int main()
{
freopen("num.in","r",stdin);
freopen("num.out","w",stdout);
t=read();
while (t--)
{
n.len=0;
ans=1;
char ch[2018];
scanf("%s",ch);
for (int i=strlen(ch)-1;i>=0;i--) n.s[++n.len]=ch[i]-'0';
if (n.len==1 && n.s[1]==1)
{
printf("1\n");
continue;
}
while (n.len>=1)
{
ans=(ans*val[n.s[1]])%10;
int sum=0,len=n.len;
for (int i=len;i;i--)
{
sum=sum*10+n.s[i];
n.s[i]=sum/5;
sum%=5;
}
while (!n.s[len]) len--;
n.len=len;
sum=n.s[1]+n.s[2]*10;
if (sum%4==0) ans=(ans*6)%10;
else if (sum%4==1) ans=(ans*8)%10;
else if (sum%4==2) ans=(ans*4)%10;
else if (sum%4==3) ans=(ans*2)%10;
}
printf("%d\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}
拐弯(ddos)
洛谷题目:P1649 [USACO07OCT]障碍路线Obstacle Course
真·玄学题目
我在11:23的时候花5分钟写了个DFS,然后忘了回溯爆0了。。。加了一句话多了60分。代码长这样:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
inline int read()
{
char ch=getchar();
int f=1,x=0;
while (ch<'0' || ch>'9')
{
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
int n,mp[105][105],st[2],ed[2],fx[4]={1,0,-1,0},fy[4]={0,1,0,-1},ans;
bool vis[105][105];
void dfs(int x,int y,int last,int now)
{
if (x==ed[0] && y==ed[1])
{
vis[x][y]=1;
ans=min(ans,now);
return;
}
if (vis[x][y]) return;
vis[x][y]=1;
for (int i=0;i<4;i++)
{
int tx=x+fx[i],ty=y+fy[i];
if (tx<1 || tx>n || ty<1 || ty>n || mp[tx][ty]==0) continue;
int sum;
if (i==last || last==-1) sum=0;
else sum=1;
dfs(tx,ty,i,now+sum);
}
vis[x][y]=0;
}
int main()
{
n=read();
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
char ch;
cin>>ch;
if (ch=='.') mp[i][j]=1;
else if (ch=='x') mp[i][j]=0;
else if (ch=='A') st[0]=i,st[1]=j,mp[i][j]=1;
else ed[0]=i,ed[1]=j,mp[i][j]=1;
}
}
ans=1e9;
dfs(st[0],st[1],-1,0);
if (!vis[ed[0]][ed[1]]) printf("-1");
else printf("%d",ans);
return 0;
}
这真的是我5分钟打出来的!除了头文件和快读是粘贴的。看来我搜索没有那么蒻了。
然后改成BFS就能过。。。只需要注意为了保证拐弯次数递增,每次需要把当前方向走到底再换方向走
AC代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define pr pair<int,int>
#define mpr make_pair
using namespace std;
inline int read()
{
char ch=getchar();
int f=1,x=0;
while (ch<'0' || ch>'9')
{
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
int n,mp[105][105],st[2],ed[2],fx[4]={1,0,-1,0},fy[4]={0,1,0,-1},ans[105][105];
inline void bfs()
{
queue <pr> q;
q.push(mpr(st[0],st[1]));
ans[st[0]][st[1]]=0;
while (!q.empty())
{
int x=q.front().first,y=q.front().second; q.pop();
if (x==ed[0] && y==ed[1]) break;
for (int i=0;i<4;i++)
{
int tx=x,ty=y;
for (;;)
{
tx+=fx[i]; ty+=fy[i];
if (tx<1 || tx>n || ty<1 || ty>n || !mp[tx][ty]) break;
if (ans[x][y]+1<ans[tx][ty])
{
ans[tx][ty]=ans[x][y]+1;
q.push(mpr(tx,ty));
}
}
}
}
if (ans[ed[0]][ed[1]]==1e9) printf("-1");
else printf("%d",ans[ed[0]][ed[1]]-1);
return;
}
int main()
{
n=read();
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
ans[i][j]=1e9;
char ch;
cin>>ch;
if (ch=='.') mp[i][j]=1;
else if (ch=='x') mp[i][j]=0;
else if (ch=='A') st[0]=i,st[1]=j,mp[i][j]=1;
else ed[0]=i,ed[1]=j,mp[i][j]=1;
}
}
bfs();
return 0;
}
赛马(horse)
洛谷题目:P1650 田忌赛马
贪心。
- 开始也是先排序,可以使用sort快排
- 然后将田忌最大的马与国王进行比较
- 如果田忌最大的马大于国王,那么就胜场++
- 如果田忌最大的马小于国王,那么就一定会输,所以用田忌最小的马输给国王最大的马
- 如果田忌最大的马等于国王,那么就比较最小的马
- 如果田忌最小的马大于国王,那么胜场++
- 如果田忌最小的马小于国王,那么就输给国王
- 如果田忌最小的马等于国王,就用田忌最小的马对国王最大的马,如果国王最大的马大,那么财产要减200
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
inline int read()
{
char ch=getchar();
int f=1,x=0;
while (ch<'0' || ch>'9')
{
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
int n,x[2005],y[2005],ans;
inline bool cmp(int x,int y)
{
return x>y;
}
int main()
{
n=read();
for (int i=1;i<=n;i++) x[i]=read();
for (int i=1;i<=n;i++) y[i]=read();
sort(x+1,x+n+1,cmp);
sort(y+1,y+n+1,cmp);
int i=1,j=n,k=1,l=n;
while (i<=j)
{
if (x[i]>y[k])
{
ans+=200;
i++; k++;
}
else if (x[i]<y[k])
{
ans-=200;
j--; k++;
}
else if (x[j]>y[l])
{
ans+=200;
j--; l--;
}
else
{
if (x[j]<y[k]) ans-=200;
j--; k++;
}
}
printf("%d",ans);
return 0;
}
总结
我今天的第一次提交分数是100+50+10+10=170,其中低级失误占的分数有50分,也就是那个忘了回溯。当然,这也反映出来我时间安排不当的问题。耗费大量时间在第二题,甚至连比较简单的贪心的第四题都没怎么写,而且第二题因为复杂度差了一点也只有50分。
总的来说现在低级失误还是越来越少了,但还是没有消失,所以还要多刷题啊!
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
This work is licensed under a CC BY-NC-SA 4.0 International License.
本文链接:https://llf0703.com/p/contest-summary-zj6.html