BestCoder Round #75

Posted forever97‘s blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BestCoder Round #75相关的知识,希望对你有一定的参考价值。

A题题目大意,一个给定矩形,每次一刀切出一个正方形,问可以切几刀

显然GCD改改就好。

一看数据范围,这不是C++入门练习题了么?那么GCD也懒得写了,于是噼里啪啦A了

为什么是GCD,题解的图很棒地解释了这个

技术分享

发现以后这之类的问题说明都可以看看这张图。

暴力的代码:

#include <cstdio>
using namespace std;
int m,n,ans;
int main(int T){
    scanf("%d",&T);
    while(T--){
        ans=0;
        scanf("%d%d",&n,&m);
        while(n!=m){
            if(n>m)n-=m;
            else m-=n;
            ans++;
        }ans++;
        printf("%d\n",ans);
    }return 0;
}

GCD代码:

#include <cstdio>
#include <algorithm>
using namespace std;
int ans=0,T,a,b;
int gcd(int a,int b){return (b==0?0:(ans+=a/b,gcd(b,a%b)));}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&a,&b);
        ans=0;gcd(a,b);
        printf("%d\n",ans);
    }return 0;
}

B题题意:普通的手机画图解锁方式,问是否正确,就是= =,嗯,喜闻乐见的模拟题

坑点略多:长度大于等于4,数字是1~9之间,嗯,没了。但是卡了一个小时,做完CD两题才回来过掉,还是做题不够细致啊

#include <cstdio>
#include <cstring>
using namespace std;
int f[15],n,T,a[15],v[15],map[15][15],pre;
void change(int x){
    if(x==2){map[1][3]=1;map[3][1]=1;}
    if(x==5){
        map[4][6]=1;map[6][4]=1;
        map[2][8]=1;map[8][2]=1;
        map[1][9]=1;map[9][1]=1;
        map[3][7]=1;map[7][3]=1;
    }
    if(x==8){map[7][9]=1;map[9][7]=1;}
    if(x==4){map[1][7]=1;map[7][1]=1;}
    if(x==6){map[3][9]=1;map[9][3]=1;}
}
void init(){
    map[1][3]=0;map[3][1]=0;
    map[4][6]=0;map[6][4]=0;
    map[2][8]=0;map[8][2]=0;
    map[1][9]=0;map[9][1]=0;
    map[3][7]=0;map[7][3]=0;
    map[7][9]=0;map[9][7]=0;
    map[1][7]=0;map[7][1]=0;
    map[3][9]=0;map[9][3]=0;
}
int main(){
    scanf("%d",&T);
    while(T--){
        memset(v,0,sizeof(v));
        scanf("%d",&n);
        for(int i=1;i<=9;i++)for(int j=1;j<=9;j++){map[i][j]=1;}
        init();
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        int flag=1; 
        if(0<a[1]&&a[1]<=9){change(a[1]);v[a[1]]=1;} 
        pre=a[1]; 
        if(n<4)flag=0;
        for(int i=2;i<=n;i++){
            if(pre>9||pre<1){flag=0;break;}
            if(a[i]<1){flag=0;break;} 
            if(a[i]>9){flag=0;break;}
            if(map[pre][a[i]]&&!v[a[i]]){
                pre=a[i]; change(a[i]);
                v[a[i]]=1;
            }else{flag=0;break;}
        }puts(flag?"valid":"invalid");
    }return 0;
}

C题是排列组合题,求不能出现连续三个相同的排列有几种

看看数据范围,首先最裸的递推能过,然后想想优化……

等等,不对,最裸的递推能过,那么交吧= =

递推的方法是记录结尾不同字母,不同长度相同的有几种,然后分类继承就好。

有点像多态继承dp,但是还是认为这是简单递推吧。

#include <cstdio>
const int mod=1000000007;
int dp[2005][30][5],ans,n,T;
int main(){
    for(int i=1;i<=26;i++)dp[1][i][1]=1;
    for(int i=2;i<=2000;i++){
        for(int j=1;j<=26;j++){
            for(int k=1;k<=26;k++){
               if(j!=k){for(int u=1;u<=3;u++){dp[i][j][1]=(dp[i][j][1]+dp[i-1][k][u])%mod;}}
               else{
                   dp[i][j][2]=(dp[i][j][2]+dp[i-1][k][1])%mod;
                   dp[i][j][3]=(dp[i][j][3]+dp[i-1][k][2])%mod;
               }
            }
        }
    }scanf("%d",&T);
    while(T--){
        scanf("%d",&n); ans=0;
        for(int i=1;i<=26;i++)for(int j=1;j<=3;j++)ans=(ans+dp[n][i][j])%mod;
        printf("%d\n",ans);
    }return 0;
}

然后同学说空间可以优化啊,好咯,那就优化一下空间,不过比赛的时候,上面那段代码就能过掉了。

#include <algorithm>
#include <cstdio>
using namespace std; 
int mod=1000000007;
int f[2010],T;
void init(){
    f[0]=1;
    for(int i=1;i<=2000;i++){
        for(int j=max(i-3,0);j<i;j++){
            if(j)f[i]+=(1LL*f[j]*25)%mod;
            else f[i]+=(1LL*f[j]*26)%mod;
            f[i]%=mod;
        }
    }
}
int main(){
    for(init(),scanf("%d",&T);T--;){
        for(int n;~scanf("%d",&n);){printf("%d\n",f[n]);}
    }return 0;
}

考虑从前三位已知答案直接推导到现在这一位的答案,预处理所有答案即可,考虑到过程超过int,

那么中间暂时用longlong存一下再取模回来num<1e9+7,num*2<INT_MAX,那么int存储答案可行。

空间优化到O(n),时间效率O(n^2)。

D题约瑟夫问题,嗯,约瑟夫问题,我不会,曾经有段时间是会的来着,但是现在忘了。

看看数据范围5000,那么暴力打表吧,于是开心地开始打表了。

打表程序没啥好说的,就是个暴力双向链表 (⊙…⊙)。

#include <cstdio>
#include <algorithm>
using namespace std;
int ans[5005],pre[5005],v[5005],nxt[5005],cnt,d,now;
int main(){
    freopen("D.out","w",stdout); 
    for(int n=1;n<=5000;n++){
        memset(v,0,sizeof(v));
        now=1;
        for(int i=1;i<=n;i++)pre[i]=i-1,nxt[i]=i+1;
        nxt[n]=1; pre[1]=n; v[1]=1; pre[2]=n; nxt[n]=2; 
        for(int d=2;d<n;d++){
            for(int i=1;i<=d;i++)now=nxt[now];
            nxt[pre[now]]=nxt[now]; pre[nxt[now]]=pre[now]; v[now]=1; 
        }for(int i=1;i<=n;i++)if(v[i]==0)ans[n]=i;
    }for(int i=1;i<=5000;i++)printf("%d,",ans[i]);
    return 0;
}

打出来一交就ok了。

出于对算法的尊重,我们回顾一下约瑟夫问题。

解决问题的主要思想是重标号,对于每次除去人之后的约瑟夫环,我们重新标号,将其下一个作为0号

可能会问,为什么不用1号呢,因为0号的话程序处理起来会十分方便,而起点为1取模起来就很尴尬了。

那么重标号之后,约瑟夫n元环就变成了约瑟夫n-1元环,子问题,那么递推模型就出来了,

所以,经典的约瑟夫环公式为f[i]=(f[i-1]+k)%i

现在回到这道题目,增加了一维,而且每次移动的位置不是固定的,怎么做?

当然还是递推啦,汉诺塔的变种还是汉诺塔,那么约瑟夫环的变种当然最简单的解法还是从原始的约瑟夫环算法入手

加上一维,就是递推式了 f[i][j]=(f[i-1][j+1]+j)%i

那么,n^2预处理,就可以了,数组开不下?那就滚动一下

#include <cstdio>
int f[2][5005],n,T,ans[5005],t;
int main(){
    for(int i=1;i<=5000;i++)f[1][i]=0; ans[1]=1; t=1;
    for(int i=2;i<=5000;i++){
        t^=1;
        for(int j=5000;j>=1;j--)f[t][j]=(f[t^1][j+1]+j)%i;
        ans[i]=f[t][1]+1;
    }scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        printf("%d\n",ans[n]);
    }return 0;
}

当然,偷懒一点干脆什么都不存,直接一维乱搞也是可以的:

#include <cstdio>
int T,x,n;
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);x=0;
        for(int i=2;i<=n;i++)x=(x+n-i+1)%i;
        printf("%d\n",x+1);
    }return 0;
}

显然不管怎么看,都是比我暴力的链表要好写许多,约瑟夫算法,受教了。

E题根本就没有时间看嘛,果然还是代码能力太差,死磕在B题。

什么时候能毫无遗憾地说我尽力了,但是真的做不出来,而不是,天呐我又犯错了,也许就真的离大神不远了。

E题的题目大意是,每天需要有一定量的飞行员表演,现在国王手头有一定量的飞行员,当然他还可以付出一定代价招募新的飞行员,

但是新的飞行员只有在一定时间后才可以来表演,那么现在问题来了,飞行员可傲娇了,不要连续工作两天,那么机智的国王想出了一个政策,在si天工作后的飞行员可以在ti天后再回来听从号令,同时获得一定量的薪水(嗯,加班费= =),那么现在,想要每天都有飞行员表演(国王脑子里在想啥),需要的最小的费用是多少(最小费用是付出的薪水和招募的费用总和)。

= =好像题目描述被我变长了,不管啦~

嗯,现在分析一下模型,每天要有飞行员遛飞机,那么就是要求按权重覆盖全图,求费用最小

好咯,最小费用最大流

接下来的问题就是如何去建图

网络中一个点需要被遍历,是怎么做的?对,拆点。

从源点连每天需要的飞行员的流量到每天需求的点,然后每天完成的点连需要的飞行员流量到汇点

那么如果满流,就说明方案可行

为了说明方便,记每天需求的点(即直接与源点相连的点为X点),每天完成的点(即直接与汇点相连的点为Y点)

对于每种休假方案,建立一条Xsi到Ysi+ti的边,费用为薪水,流量为INF,

对于招聘计划,从源点向大于P天的Y点引费用为聘用的钱,流量为INF的边

当然一开始S点是可以直接流向Y1的,所以建一条费用为0,流量为k(初始拥有飞行员数量)的边

然后相邻的Y之间连0费用无限流量的边,

建图完毕,跑一遍费用流,流量等于总需求则输出最小费用,否则无解

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int S=0,T=1001,inf=0x7fffffff,N=1005;
#define rep(i,n) for(int i=1;i<=n;i++)
int P,Q,n,m,cnt=0,ans,s[N],t[N],d[N],q[N],from[N],g[N],p[N],flow,tot,k;
bool in[1005];
struct edge{int from,to,nxt,c,v;}e[100001];
void add(int u,int v,int w,int c){
    e[++cnt].from=u;e[cnt].to=v;
    e[cnt].nxt=g[u];g[u]=cnt;
    e[cnt].c=c;e[cnt].v=w;
}void insert(int u,int v,int w,int c){add(u,v,w,c);add(v,u,0,-c);}
bool spfa(){
    for(int i=0;i<=T;i++)d[i]=inf;
    int t=0,w=1;d[0]=0;in[0]=1;q[0]=0;
    while(t!=w){
        int now=q[t];t++;if(t==T)t=0;
        for(int i=g[now];i;i=e[i].nxt)
            if(e[i].v&&d[e[i].to]>d[now]+e[i].c){
                d[e[i].to]=d[now]+e[i].c;from[e[i].to]=i;
                if(!in[e[i].to]){in[e[i].to]=1;q[w++]=e[i].to;if(w==T)w=0;} 
            }in[now]=0; 
    }return(d[T]!=inf);
}
void mcf(){
    int x=inf;
    for(int i=from[T];i;i=from[e[i].from])x=min(x,e[i].v);flow+=x;
    for(int i=from[T];i;i=from[e[i].from]){e[i].v-=x;e[i^1].v+=x;ans+=e[i].c*x;}
}
int main(int cas){
    scanf("%d",&cas);
    while(cas--){
        memset(g,0,sizeof(g));
        memset(e,0,sizeof(e));
        ans=flow=tot=0;
        scanf("%d%d",&n,&k);cnt=1;ans=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&p[i]);
            tot+=p[i];
        }scanf("%d%d%d",&m,&P,&Q);
        rep(i,m)scanf("%d%d",&s[i],&t[i]);
        rep(i,n)insert(S,i,p[i],0),insert(i+n,T,p[i],0);
        insert(S,n+1,k,0);rep(i,n)insert(i+n,i+n+1,inf,0);
        for(int i=max(1,P);i<=n;i++)insert(S,i+n,inf,Q);
        rep(i,n)rep(j,m)if(i+t[j]<=n)insert(i,i+t[j]+n,inf,s[j]);
	      while(spfa())mcf();
	      if(flow!=tot)puts("No solution");
        else printf("%d\n",ans);
	  }return 0;
}

好像这次BC的难度相比于上两次有所下降,但是= =,水题也更能体现手速,调试能力和思维差距。

连续几次掉分之后,rating终于又回升到1900+,还是有点小开心的。

感冒有点重,这几天浑浑噩噩,敲敲代码,喝喝热水。

但是好歹雨天在钟表上转了几圈,还是走远了,午后的阳光,暖到酥骨。

愿我拥有足够多的好心情,颠沛流离后还能调笑过往。

 

以上是关于BestCoder Round #75的主要内容,如果未能解决你的问题,请参考以下文章

BestCoder Round #75 - King's Phone

BestCoder Round #75 解题报告

BestCoder Round #75 King&#39;s Order dp:数位dp

BestCoder Round #92

BestCoder Round #88

BestCoder Round #90