2017-10-7离线赛题解

题A:
记x的位数之和为A(x)
则满足: A(x2)=A(x)A(x)

这道题的暴力写法是枚举左区间到右区间的所有数
但由于r<=1e9
这样的时间复杂度只能水80分
观察x可知,x*x最多为1e18
则A(x*x)最大为18*9=162
则A(x)只有<=12时才能满足 A(x2)=A(x)A(x)
所以可以dfs穷举
枚举每个数位的数(0-9)ps:注意判断第一位


题B:
第二题有几个需要挖掘的特性:
1.炮弹是从右往左打的,最后一个坦克在最左边
2.坦克会打掉最右边一个区间的坦克

可以二分查找每个坦克能打到最远的坦克
然后下一个能发射炮弹的就是最远坦克左边的第一个坦克
且我们可以得出,每个坦克点所能打到的坦克数是固定的(也可以得出在这个坦克左边第一个发射炮弹的坦克)

我们若从左往右处理,记录从它开始发射炮弹能打到的坦克数,
而由于我们的预处理顺序,我们询问到的都已被预处理过,那么只用算出最远被打到的坦克的位置
状态转移方程如下:
dp[i]=dp[j1]+(ij)
i为当前坦克,j为此坦克能打到的最远坦克
那么复杂度就是遍历n*二分查找logn


题C
分析数据可知,n<=100,m<=10
m小的可怜,应该在列上做文章
则估计题目写法应是枚举行数,转移每一行的情况
可以推断是一个状压dp
我们只关心当前行的状态上一行的状态上上行的状态
若单纯根据m=10枚举状态,则状态数为2^10
但仔细推导,其实状态数根本没有那么多,如两两之间间隔至少为2,山地不能放炮兵,这样状态数极限为60个
复杂度枚举当前行 * 枚举上一行 * 枚举上上行 * 行数
也就是 603100

具体代码实现如下:

#include<stdio.h> 
#include<string.h>
char str[105][105]; 
int n,m;
int dp[110][65][65];//dp记录的是士兵数 
int cnt[1100];
int sts[105];
int ok[65],tc=0;
void Init() {//预处理每一种方案 
    for(int i=0; i<(1<<m); i++) {
        if(i&(i>>1))continue;//判断右边一位有没有士兵 
        if(i&(i>>2))continue;//判断右边两位有没有士兵 
        if(i&(i<<1))continue;//判断左边一位有没有士兵 
        if(i&(i<<2))continue;//判断左边两位有没有士兵 
        ok[tc++]=i;
        for(int j=0; j<m; j++)//记录这种方案有几个士兵
            if(i&(1<<j))cnt[i]++; 
    }
}
void check(int &x,int y) {
    if(x==-1||x<y)x=y;
}
int main() { 
    scanf("%d %d",&n,&m);
    Init();
    for(int i=1; i<=n; i++) {
        scanf("%s",str[i]);
        for(int j=0; j<m; j++)
            if(str[i][j]=='H')sts[i]|=(1<<(m-j-1));
            //把山地预处理出来 在二进制中,若为一,则有山地 
    }
    memset(dp,-1,sizeof(dp));
    dp[0][0][0]=0;
    for(int i=0; i<n; i++) {//枚举行 
        for(int j=0; j<tc; j++) //枚举上上行方案 
            for(int k=0; k<tc; k++) {//枚举上一行的方案 
                if(ok[j]&ok[k])continue;//判断上上行与上一行是否冲突 
                if(dp[i][j][k]==-1)continue;//判断这种情况是否存在 
    //dp[i][j][k]表示前i行放完,第i-1行放坦克情况为j,第i行状态为k
                for(int ii=0; ii<tc; ii++) {
                    if(ok[ii]&sts[i+1])continue;//判断士兵是否在山地上 
                    if(ok[j]&ok[ii])continue;//判断士兵是否与上上行冲突 
                    if(ok[k]&ok[ii])continue;//判断士兵是否与上一行冲突
                    check(dp[i+1][k][ii],dp[i][j][k]+cnt[ok[ii]]);
                    //更新dp
                }
            }
    }
    int ans=0;
    for(int i=0; i<tc; i++)
        for(int j=0; j<tc; j++){ 
            check(ans,dp[n][i][j]);
        }
    printf("%d\n",ans);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值