2017-10-7离线赛题解

来源:互联网 发布:it专业大学排名 编辑:程序博客网 时间:2024/05/21 09:59

题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;}