2017.10.7离线赛总结

来源:互联网 发布:网络通信安全员证书 编辑:程序博客网 时间:2024/05/17 23:08

第1题:S数
题意:定义S(x)为x每位上的数字和。eg:S(123)=1+2+3=6;S(22)=2+2=4
如果S(x*x)=S(x)*S(x)
则称x为S数,如x=22,S(x)=4,S(x*x)=S(484)=16;所以22是一个S数。
求[L,R]范围内S数的个数。

数据范围:
80%,R<=10^5;
100%,R<=10^9。

思路:当时只打了暴力(然而暴力数组炸了),打表找规律,发现s数只有是个位数是0,1,2,3;所以只需要从1,2,3去dfs。

#include<bits/stdc++.h>#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)#define LL long longusing namespace std;int l,r,ans;struct P80{    int get(LL x){        int sum=0;        while(x>0)sum+=x%10,x/=10;        return sum;     }    void solve(){        REP(i,l,r)if(get(i)*get(i)==get(i*i))ans++;        cout<<ans<<endl;     }}p80;struct P100{    int get(LL x){        int sum=0;        while(x>0)sum+=x%10,x/=10;        return sum;     }    void fun(LL x){        if(x>r)return;        int a=get(x),b=get(x*x);        if(x>=l && a*a==b)ans++;        REP(i,0,3)fun(x*10+i);     }    void solve(){        REP(i,1,3)fun(i);        cout<<ans<<endl;    }}p100;int main(){    scanf("%d%d",&l,&r);//  if(r<=100000)p80.solve();    p100.solve();    return 0;}

第2题:黑客入侵
题意:有n辆坦克,编号1到n,排成一条直线,黑客军团恰在此时入侵了系统,使得从右到左的坦克都依次向左开炮,先是第n辆坦克开火,如果第n-1辆坦克不在第n辆坦克的射程以内,第n-1辆坦克再开火,如果第n-2辆坦克没有被前面辆坦克摧毁,继续开火。。。依次进行下去。
使坦克最少被摧毁,只使用最后一辆备用坦克,把备用坦克放在这n辆坦克的右边,先开火,摧毁右边的一些坦克。
请你算算,如何安排这辆备用坦克的位置和射程,使得坦克损失最少。

数据范围:
70%,n<=2000;
100%,n<=200000。

思路:枚举坦克的位置,答案即为min(前i个坦克的损失量+后面的坦克数量)。这样暴力O(n^2)70,而进一步发现被摧毁的坦克是一段连续的区间,即可用dp或二分。

#include<bits/stdc++.h>#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)#define N 200005#define INF 0x3f3f3f3fusing namespace std;int n,ans=INF;int p[N],r[N];int dp[N][2];bool mark[N];struct P70{    void work(int x){        int sum=n-x;        memset(mark,0,sizeof(mark));        DREP(i,x,2){            if(mark[i])continue;            int t=i;            while(p[t]-r[t]<=p[i-1] && i>=2)i--,sum++;        }        ans=min(ans,sum);    }    void solve(){        REP(i,1,n)work(i);        cout<<ans<<endl;    }}p70;struct P100{    void solve(){        REP(i,1,n){            int x=max(0,p[i]-r[i]);            int y=lower_bound(p+1,p+1+n,x)-p;            dp[i][0]=max(dp[i-1][0],dp[i-1][1]);            dp[i][1]=dp[y-1][1]+1;        }         cout<<min(n-dp[n][0],n-dp[n][1])<<endl;    }}p100;int main(){//  freopen("tank.in","r",stdin);//  freopen("tank.out","w",stdout);     scanf("%d",&n);    REP(i,1,n)scanf("%d%d",&p[i],&r[i]);    if(n<=2000)p70.solve();    else p100.solve();    return 0;}

第3题:炮兵阵地
题意:n*m的网络地图,P表示平原,H表示山地。一支炮兵部队可在部署在平原上,且它的攻击范围为前2格、后2格、左2格,右2格。要保证自己部署的炮兵不能攻击自己,那么最多能部署多少支部队。

数据范围:
100%,n<=100,m<=10。

思路:一支炮兵部队影响的范围如题意所述,那么就考虑上一层和上上一层。
就想到了状压dp。但很惆怅..dfs没有输出,卡了很久,就根本没去想正解。

#include<bits/stdc++.h>#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)#define N 105#define M 15using namespace std;int n,m,tot,ans;char s[N][M]; bool mark[N][M];int pos[N*10],sum[N*10],A[N];int dp[N][N][N];void chkmax(int &x,int y){if(x==-1 || x<y)x=y;}void chkin(int &x,int y){if(x==-1 || x>y)x=y;}void Init(){    memset(dp,-1,sizeof(dp));    REP(i,0,(1<<m)-1){        if(i&(i<<1) || i&(i<<2) || i&(i>>1) || i&(i>>2))continue;        pos[++tot]=i;        sum[tot]=__builtin_popcount(i);//求二进制中1的个数     }       dp[0][1][1]=0;}int main(){    scanf("%d%d",&n,&m);    REP(i,1,n){        scanf("%s",s[i]);        REP(j,0,m-1)if(s[i][j]=='H')A[i]|=1<<j;    }    Init();    REP(i,0,n-1){        REP(j,1,tot){            REP(k,1,tot){                if(pos[j]&pos[k] || dp[i][j][k]==-1)continue;                 REP(l,1,tot){                    if(pos[j]&pos[l] || pos[k]&pos[l] || pos[l]&A[i+1])continue;                    chkmax(dp[i+1][k][l],dp[i][j][k]+sum[l]);                }            }        }    }    REP(i,1,tot)        REP(j,1,tot)            chkmax(ans,dp[n][i][j]);    cout<<ans<<endl;    return 0;}

小结:
这次考得很差,无论是第1题的数组访问越界,还是第2题和第3题没想到位,都毫无保留地体现自己的思维很不严密,而且这次还是忘了在源代码前打(LL,mod,内存,调试,文件名)。

原创粉丝点击