20171007离线赛总结

考试时的思路:

第一题先循环水一个80分出来
 
第二题先水70分,再用倍增枚举每一个坦克对应的下一个坦克。
 
第三题直接上DFS,能拿多少拿多少。

题解:

第一题 S数

  这道题,我打了个表,然后用二分法来做,记录每个答案的位置,即可得解。但是最后时间不够了,我发现lower_bound用错了的时候只剩下4分钟了,匆忙修改,但还是没对,不过好在暴力分拿到了,看来先打暴力这个方法肯定没错。
  

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#define M 100086
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
int sum[M];
void Init() {
    FOR(i,1,M-1) {
        int x=i;
        while(x) {
            sum[i]+=x%10;
            x/=10;
        }
    }
}
int n,m;
int A[]= {}//打表
void solve1() {
    Init();
    int ans=0;
    FOR(i,n,m) {
        long long x=i*i;
        int cnt=0;
        while(x) {
            cnt+=x%10;
            x/=10;
        }
        if(cnt==sum[i]*sum[i])ans++;
    }
    cout<<ans<<endl;
}
void solve2() {
    int x,y;
    y=lower_bound(A+1,A+sizeof(A)/4,m)-A-1;
    x=lower_bound(A+1,A+sizeof(A)/4,n)-A-1;
    cout<<y-x<<endl;
}
int main() {
    cin>>n>>m;
    if(m<=1e5)solve1();
    else solve2();
    solve2();
    return 0;
}

  
  这道题有没有更好的算法呢?
  显然有。
  首先我们分析一下最大的数也就是18个9,那么加起来也就是9*18,开个方也就在12左右,只要根据这个来DFS就可以了,还是非常简单的,就看这一点有没有想到。

第二题 黑客入侵

  这道题,首先暴力就70分,少了点儿,因此我就考虑到了看电视这道题,用同样的思路,每一个坦克所对应的下一辆坦克是固定的,因此只要用倍增法求出有多少辆坦克没被打到,就可以顺势求出有多少辆坦克被打了。这种算法,还真没人这么写过,虽然麻烦了点,但正确性显然啊,特别是熟练使用倍增之后,如同行云流水一般,刷刷刷代码就打完了。
  

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#define M 200050
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
struct node {
    int pos,attack;
} A[M],B[M];
int ans=1e9;
bool mark[M];
int n;
int Fa[20][M];
void solve1() {
    DOR(i,n+1,1) {
        int cnt=n+1-i;
        memset(mark,0,sizeof(mark));
        DOR(j,i-1,1) {
            if(mark[j])continue;
            int x=A[j].pos-A[j].attack;
            DOR(k,j-1,1) {
                if(A[k].pos>=x) {
                    mark[k]=true;
                    cnt++;
                } else break;
            }
        }
        if(cnt<ans)ans=cnt;
    }
    cout<<ans<<endl;
}
int cnt[20][M];
void Init() {
    FOR(i,1,n)B[i]=A[n-i+1];
    FOR(i,1,n) {
        int x=B[i].pos-B[i].attack;
        FOR(j,i+1,n) {
            if(B[j].pos>=x)continue;
            Fa[0][i]=j;
            break;
        }
    }
    FOR(i,1,n)cnt[0][i]=1;
    FOR(j,1,19)FOR(i,1,n) {
        Fa[j][i]=Fa[j-1][Fa[j-1][i]];
        cnt[j][i]=cnt[j-1][i]+cnt[j-1][Fa[j-1][i]];
    }
}
void solve2() {
    Init();
    FOR(i,0,n-1) {
        int tmp=0;
        int t=i+1;
        FOR(j,0,19) {
            tmp+=cnt[j][t];
            t=Fa[j][t];
        }
        if(n-tmp<ans)ans=n-tmp;
    }
    cout<<ans<<endl;
}
int main() {
    cin>>n;
    FOR(i,1,n)scanf("%d%d",&A[i].pos,&A[i].attack);
    if(n<=2000)solve1();
    else solve2();
    return 0;
}

  其实其他的大佬的代码比我更精简,想法也不错,所以可以看一看zhowie大佬的博客以及YZK大佬的博客

第三题:炮兵

  这道题我DFS写错了,但是迷之水了40分,我也很无奈啊。
  错误示范:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
int n,m;
bool pic[120][20];
char s[20];
bool mark[120][20];
int ans=0;
bool tmp[120][20];
void dfs(int x,int y,int cnt) {
    if(cnt>ans)ans=cnt;
    FOR(i,1,n)FOR(j,1,m)tmp[i][j]=mark[i][j];
    FOR(i,1,n)FOR(j,1,m)if(!mark[i][j]) {
        int top=max(1,i-2);
        int tail=min(i+2,n);
        FOR(k,top,tail)mark[k][j]=true;
        int top1=max(1,j-2);
        int tail1=min(j+2,m);
        FOR(k,top1,tail1)mark[i][k]=true;
        dfs(i,j,cnt+1);
        FOR(k,top,tail)mark[k][j]=tmp[k][j];
        FOR(k,top1,tail1)mark[i][k]=tmp[i][k];
    }
}
int main() {
    cin>>n>>m;
    FOR(i,1,n) {
        scanf("%s",s+1);
        FOR(j,1,m)pic[i][j]=(s[j]=='H');
    }
    FOR(i,1,n)FOR(j,1,m)mark[i][j]=pic[i][j];
    dfs(0,0,0);
    cout<<ans<<endl;
    return 0;
}

  可以清楚地看到,在还原现场的时候,看上去好像没什么错,用tmp数组来维护原来的数组,但是,在往下dfs的时候,tmp数组又重新赋值了,导致tmp数组用了跟没用一样,这就解释了为什么我接下来造了一组100×10的数据,每个都是P,却3毫秒跑出来。但是,水了40分过来,这次可以说是侥幸,下次如果数据比较强的话,就没那么好的运气了,这点要注意,千万不能让暴力的分数也丢了。
  这道题的正解是状态压缩dp,每一个点只与前一个点,以及上面第二个点有关系,因此可以用dp[i][j][k]来表示前i行放完,第i-1行放坦克情况为j,第i行状态为k的情况,具体正解,请见YZK大佬的代码。
  

#include<iostream>
#include<cstdio>
#include"cstring"
#define M 105
#define FOR(i,a,b) for(register int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(register int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
int pic[M][15];
int dp[M][65][65];
int flag[65];
char s[15];
void checkmax(int &a,int k) {
    if(a==-1 or a<k)a=k;
}
int cnt[M*10];
int n,m;
int t=0;
void create() {
    FOR(i,0,(1<<m)-1) {
        if(i&(i<<1))continue;
        if(i&(i<<2))continue;
        if(i&(i>>1))continue;
        if(i&(i>>2))continue;
        flag[++t]=i;
        FOR(j,0,m-1)if(i&(1<<j))cnt[i]++;
    }
}
int ShimaKZ[M];
int ans=0;
int main() {
    cin>>n>>m;
    create();
    memset(dp,-1,sizeof(dp));
    FOR(i,1,n) {
        scanf("%s",s+1);
        FOR(j,1,m)if(s[j]=='H')ShimaKZ[i]|=(1<<(m-j));
    }
    dp[0][1][1]=0;
    FOR(i,1,n)FOR(j,1,t)FOR(k,1,t) {
        if(flag[j]&flag[k])continue;
        if(not(~dp[i-1][j][k]))continue;
        FOR(l,1,t) {
            if(flag[j]&flag[l])continue;
            if(flag[k]&flag[l])continue;
            if(flag[l]&ShimaKZ[i])continue;
            checkmax(dp[i][k][l],dp[i-1][j][k]+cnt[flag[l]]);
        }
    }
    FOR(i,1,t)FOR(j,1,t)checkmax(ans,dp[n][i][j]);
    cout<<ans<<endl;
    return 0;
}

总结:

  这次考得一般吧,该水的分都水过来了,有些小问题还是要注意,还有一些解法要总结一下。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值