NOIP2017普及复赛 (~~题解)

来源:互联网 发布:java导入自己的package 编辑:程序博客网 时间:2024/06/09 20:24

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

T1

我这神奇的截图

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

T2

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

代码:

# include<cstdio># include<cstring># include<algorithm>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



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

代码:

# include<cstdio># include<cstring># include<algorithm>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


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

代码:

# include<cstdio># include<cstring># include<algorithm>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;}
原创粉丝点击