2017.10.7离线赛总结

第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 long
using 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 0x3f3f3f3f
using 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 15
using 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,内存,调试,文件名)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值