NOIP2017普及复赛 (~~题解)

感觉这一次普及难度很ZZ啊。。。
怎么说呢,考差了(最后一题忘记写朴素DP丢了50分),第三题的BFS也写错了一个if(问题不大),总之,思路上没有问题,主要还是代码能力的问题。。继续加油吧

T1

NOIP2017普及复赛 (~~题解)_第1张图片

T1 智障难度。。。只要会编程应该都会做吧。。。我们就跳过此题

T2

NOIP2017普及复赛 (~~题解)_第2张图片

又是ZZ难度,嗯,普及T2,一发模拟就可以水过,数据范围 N,Q<=1000, 每一个数的值小于10000000,那么我们就可以模10判相等,复杂度也就 O(NQ) ,很容易(我先排了下序,为了保证图书编码最小嘛)

代码:

# include
# include
# include
using namespace std;
const int N = 1e3 + 10;
struct node
{
    int val,len;
}a[N];
int n,q,i,pre,len,ans;
bool cmp(node a,node b)
{
    return a.val < b.val;
}
int getans(int a,int b,int len)
{
    int val = 0,sum = 1;
    while (len--)
    {
        val += (a % 10) * sum;
        a /= 10;
        sum *= 10;
    }
    if (val == b) return 1;
    else return 0;
}
int main()
{
    scanf("%d%d",&n,&q);
    for (i = 1;i <= n; i++)
    {
        scanf("%d",&a[i].val);
        int tmp = a[i].val; len = 0;
        while (tmp)
        {
            len++;
            tmp /= 10;
        }
        a[i].len = len;
    }
    sort(a + 1,a + n + 1,cmp);
    for (i = 1;i <= q; i++)
    {
        scanf("%d%d",&len,&pre);
        ans = -1;
        for (int j = 1;j <= n; j++)
        {
            if (a[j].len < len) continue;
            if (getans(a[j].val,pre,len)) { ans = a[j].val; break; }
        }
        printf("%d\n",ans);
    }
    return 0;
}

T3

NOIP2017普及复赛 (~~题解)_第3张图片
NOIP2017普及复赛 (~~题解)_第4张图片
NOIP2017普及复赛 (~~题解)_第5张图片

难度终于上来了啊,然而这道题呢一眼最短路 or BFS,那么怎么做呢,首先考虑每个格子有颜色,我们可以想到分层图SPFA,对于不同颜色,边长为1,相同颜色边长为0,颜色为白色的,我们可以将它转换成两个点,一个黄点,一个红点,分别转移,但是我们在转移的时候记得if判一下上一次有没有用魔法。BFS同理,BFS我们设一个标记数组,代表走到某个格子是什么颜色的最小花费,然后推队列就行了(打标记的if这里考场ZZ写错了),然后注意一下细节。

代码:

# include
# include
# include
using namespace std;
const int N = 1e2 + 10;
struct node
{
    int x,y,cost,col,p;
}q[N * N * 500];
int a[N][N],bz[N][N][4],dir[4][2] = {{0,1},{1,0},{-1,0},{0,-1}};
int n,m,i,ans;
int main()
{
    memset(bz,0x3f,sizeof(bz));
    scanf("%d%d",&m,&n);
    for (i = 1;i <= n; i++)
    {
        int x,y,tmp;
        scanf("%d%d%d",&x,&y,&tmp);
        ++tmp;
        a[x][y] = tmp;
    }
    int h = 1,t = 1;
    bz[1][1][a[1][1]] = 0;
    q[h] = (node){1,1,0,a[1][1],0};
    while (h <= t)
    {
        node now = q[h++];
        for (i = 0;i < 4; i++)
        {
            int x1 = now.x + dir[i][0],y1 = now.y + dir[i][1];
            if (x1 < 1 || x1 > m || y1 < 1 || y1 > m) continue;
            if (!a[x1][y1] && now.p) continue;
            if (a[x1][y1] == now.col)
            {
                int col = now.col;
                int cost = now.cost;
                if (cost >= bz[x1][y1][col]) continue;
                bz[x1][y1][col] = cost;
                q[++t] = (node){x1,y1,cost,col,0};
            }else
            if (a[x1][y1] != now.col && a[x1][y1] != 0)
            {
                int col = a[x1][y1];
                int cost = now.cost + 1;
                if (cost >= bz[x1][y1][col]) continue;
                bz[x1][y1][col] = cost;
                q[++t] = (node){x1,y1,cost,col,0};
            }else
            if (a[x1][y1] == 0 && now.p == 0)
            {
                int col = 1;int cost = now.cost + 2;
                if (col != now.col) ++cost;
                if (cost < bz[x1][y1][col])  //这里注意不要直接continue,不然下个点不能转移,考场这里打错了。。。
                {
                    bz[x1][y1][col] = cost;
                    q[++t] = (node){x1,y1,cost,col,1};
                }
                col++; cost = now.cost + 2;
                if (col != now.col) ++cost;
                if (cost >= bz[x1][y1][col]) continue;
                bz[x1][y1][col] = cost;
                q[++t] = (node){x1,y1,cost,col,1};
            }
        }
    }
    ans = min(bz[m][m][1],bz[m][m][2]);
    if (ans > 2 * m * m) puts("-1");
    else printf("%d\n",ans);
    return 0;
}

T4

NOIP2017普及复赛 (~~题解)_第6张图片
NOIP2017普及复赛 (~~题解)_第7张图片

此次普及比较厉害的一道题,那么我们题目中直接求有两个最值,一般我们不能直接得到两个最小值,由于题目说我们是能不能到K分,那么很容易想到判定性问题,所以用二分求最小值,DP求最高分然后check。。怎么DP呢,我们可以设f[i]表示跳到i的时候能够得到的最高分,然后两个for循环,找符合范围的可转移的状态,那么这么做的复杂度是 O(N2logMax(Xi)) 的,但是也有60分,满分做法比较好想,由于转移是区间移动且单向( Xi 递增),我们就可以用单调队列来优化,我们多设一个指针,表示即将进入单调队列的数,然后来转移就行了,复杂度就变为 O(NlogMax(Xi))

代码:

# include
# include
# include
using namespace std;
const int N = 5e5 + 10;
struct node
{
    int dis,val;
}a[N];
int f[N],q[N];
int n,k,d,maxx;
bool check(int x,int xia)
{
    int h = 1,t = 1,ans = 0,i = 0,fir;
    f[0] = q[1] = 0;
    for (i = 1;a[i].dis < xia && i <= n; i++)
        continue;   //前面有一些格子的距离小于下界,则是无法跳到,直接去除
    fir = i;
    for (;i <= n; i++)
    {
        if (a[i].dis - a[i - 1].dis > (x + d)) break; //如果和前面一个格子距离已经大于上界了,则直接break就好了,后面的肯定都不能转移。
        while (a[i].dis - a[fir].dis >= xia && fir < i)
        {
            while (h <= t && f[q[t]] < f[fir]) t--;
            q[++t] = fir++;
        }
        while (h <= t && a[i].dis - a[q[h]].dis > (x + d)) h++;
        if (h > t) f[i] = -0x3f3f3f3f;  //当前格子跳不到
        else
            f[i] = f[q[h]] + a[i].val;
        ans = max(ans,f[i]);
    }
    return ans >= k;
}
int main()
{
    scanf("%d%d%d",&n,&d,&k);
    for (int i = 1;i <= n; i++)
        scanf("%d%d",&a[i].dis,&a[i].val);
    int l = 0,r = a[n].dis,ret = 0;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (check(mid,max(d - mid,1))) r = mid - 1,ret = mid;
        else l = mid + 1;
    }
    if (check(ret,max(d - ret,1))) printf("%d\n",ret);
    else puts("-1");
    return 0;
}

你可能感兴趣的:(测试,反思)