SZTU-ACM 2021 春期训练赛第一点五场 [组队赛](第四届河南大学生程序设计竞赛)总结和题解

A 序号互换

题目意思很简单,就是关于26进制的互相转换,如果是26进制换10进制,直接把每一位按顺序乘26累计到下一位即可获得最终结果。如果是10进制转26进制,我们首先要把字符串转化为整形。然后每一位对26取模。逐渐除26就能得到正确的26进制。注意临时oj不能识别万能头文件,会爆ce。但是标称给的还是万能头,大家如果要补题自己改一下就好了。

#include<bits/stdc  .h>using namespace std;char c[100001], b[30];int main(){    int t;    long long int ans;    string s, ans1;    b[0] = 'Z', b[1] = 'A';    for(int i = 2; i <= 26; i  )        b[i] = b[i - 1]   1;    cin >> t;    while(t--)    {        cin >> s;        if(s[0] >= '0' && s[0] <= '9')        {            int l = s.size();            ans = 0;            for(int i = 0; i < l; i  )                ans = ans * 10   s[i] - '0';            int k = 0;            while(ans)            {                c[k  ] = b[ans % 26];                if(ans % 26 == 0)                    ans = ans / 26 - 1;                else                    ans /= 26;            }            for(int i = k - 1; i >= 0; i--)                printf("%c", c[i]);            printf("\n");        }        else        {            int q = 0;            int ll = s.size();            for(int i = 0; i < ll; i  )                q = q * 26   (s[i] - 'A'   1);            printf("%d\n", q);        }    }    return 0;}

B 节能

这道题目很明显是一道区间dp的题目,但是很难直接设计出状态转移的方式。

dp [i] [j] [0]表示i到j的路灯都j已经关闭,机器人在路灯i的位置,此时已经消耗的最小电能

dp [i] [j] [1]表示i到j的路灯都已经关闭,机器人在路灯j的位置,此时已经消耗的最小电能

这样就可以得到状态转移方程为

dp [i] [j] [0] = min(dp[i 1] [j] [0] [i 1,j]路段以外未关闭路灯在机器人从i 1走的i期间消耗的电能,dp [i 1] [j] [1] [i 1,j]路段以外未关闭路灯在机器人从j走到i期间消耗的电能)

dp [i] [j] [1] = min(dp [i] [j-1] [0] [i,j-1]路段以外未关闭路灯在机器人从i走到j期间消耗的电能,dp [i] [j-1] [1] [i,j-1]路段以外未关闭路灯在机器人从j-1走到j期间消耗的电能)

对于某一段在某段时间里消耗的电能,我们可以通过维护一个前缀和来快速完成。推完状态转移方程,题目就很好写了,照着dp写就好了,需要注意的是,每一段的dp值需要初始化,而且要注意初始化0和1的顺序。错了就不能达到区间转移的效果,更多细节在代码里有注释,大家自行阅读理解

#include<bits/stdc  .h>using namespace std;int f[1005][1005][2], d[1005], w[1005];//f[i][j][0]:i~j之间的灯全部关闭,且机器人此时站在i处//f[i][j][1]:i~j之间的灯全部关闭,且机器人此时站在j处。int main(){    int n, k;    while(~scanf("%d", &n))    {        int sum = 0;        scanf("%d", &k);        for(int i = 1; i <= n; i  ) //对路灯功率维护前缀和 ,距离本身就是前缀和不需要自己维护        {            scanf("%d%d", &d[i], &w[i]);            sum  = w[i];            w[i]  = w[i - 1];        }        f[k][k][0] = f[k][k][1] = 0;        for(int i = k   1; i <= n; i  ) //初始化向右走的区间        {           f[k][i][1] = f[k][i - 1][1]   (d[i] - d[i - 1]) * (sum - w[i - 1]   w[k - 1]);            //从k出发最终停留到i点,是由从k出发停留在i-1点转移得到的,需要减去k到i-1部分的电费 ;            f[k][i][0] = f[k][i][1]   (d[i] - d[k]) * (sum - w[i]   w[k - 1]);             //从i回到k需要的电费;        }        for(int i = k - 1; i > 0; i--)        {            f[i][k][0] = f[i   1][k][0]   (d[i   1] - d[i]) * (sum - w[k]   w[i]);            f[i][k][1] = f[i][k][0]   (d[k] - d[i]) * (sum - w[k]   w[i - 1]);        }        for(int i = k - 1; i > 0; i--)        for(int j = k   1; j <= n; j  )        {       f[i][j][0] = min(f[i   1][j][0]   (d[i   1] - d[i]) * (sum - w[j]   w[i]),                    f[i   1][j][1]   (d[j] - d[i]) * (sum - w[j]   w[i]));       f[i][j][1] = min(f[i][j - 1][1]   (d[j] - d[j - 1]) * (sum - w[j - 1]   w[i - 1]),                    f[i][j - 1][0]   (d[j] - d[i]) * (sum - w[j - 1]   w[i - 1]));            }        printf("%d\n", min(f[1][n][0], f[1][n][1]));    }    return 0;}

C 表达式求值

这道题目应该是很经典的一道关于栈的应用,我们用一个栈存储数字,一个栈存储符号,当遇到)的时候处理一次符号运算。符号数量一定只比栈少1个,最后数字栈中剩余的数字就是我们要求的数字,注意一些处理字符串的细节就好。

#include <bits/stdc  .h>using namespace std;char a[1000];stack<int> q,f;int main(){int n,c,l;scanf("%d", &n);while(n--){scanf("%s", a);int l=strlen(a);for(int i=0;i<l;i  ){if(a[i]>='0'&&a[i]<='9'){int z=0;while(a[i]>='0'&&a[i]<='9')z = z*10 a[i]-'0',i  ;i--,q.push(z);} if(a[i]=='a'&&a[i 1]=='d')f.push(1);         if(a[i]=='m'&&a[i 1]=='i')f.push(2); if(a[i]=='m'&&a[i 1]=='a')f.push(3); if(a[i]==')'){int c;int op=f.top();f.pop();int a=q.top();q.pop();int b=q.top();q.pop();if(op==1)c=a b;if(op==2)c=min(a,b);if(op==3)c=max(a,b);q.push(c);}}printf("%d\n", q.top());}return 0;} 

D 走迷宫

题目数据范围很小,很显然题目可以用搜索解决,我们只要处理如何搜出最大差值即可,不难发现,我们可以用二分来压缩差值的空间。每一次二分答案都dfs搜一遍能否走出迷宫,需要灵活把dfs和二分联系起来。

#include <bits/stdc  .h>const int INF = 0x3fffffff;int n, maxn, minn, f = 0;int ret[150][150];int vis[150][150];int to_x[4] = {1, -1, 0, 0};int to_y[4] = {0, 0, 1, -1};inline int read_int(){    char c;    int sign = 1;    while((c = getchar()) < '0' || c > '9')        if(c == '-') sign = -1;    int res = c - '0';    while((c = getchar()) >= '0' && c <= '9')        res = res * 10   c - '0';    return res * sign;}void dfs(int x, int y, int L, int r){    if(f) return;    if(ret[x][y] > r || ret[x][y] < L) return;    if(x == n && y == n) { f = 1; return; }    for(int i = 0; i < 4; i  )    {        int xx = x   to_x[i];        int yy = y   to_y[i];        if(!vis[xx][yy] && xx >= 1 && xx <= n && yy >= 1 && yy <= n)        {            vis[xx][yy] = 1;            dfs(xx, yy, L, r);        }    }}bool judg(int x){    for(int i = minn; i <= maxn - x; i  )    {       f = 0;       memset(vis, 0, sizeof(vis));       int L = i, r = i   x;       vis[1][1] = 1;       dfs(1, 1, L, r);       if(f) return true;    }    return false;}int main(){    while(scanf("%d", &n) != EOF)    {        maxn = 0 - INF;        minn = INF;        for(int i = 1; i <= n; i  )            for(int j = 1; j <= n; j  )            {                ret[i][j] = read_int();                if(maxn < ret[i][j]) maxn = ret[i][j];                if(minn > ret[i][j]) minn = ret[i][j];            }        int L = 0, r = maxn - minn;        while(L < r)        {            int mid = (L   r) / 2;            if(judg(mid)) r = mid;            else L = mid   1;        }        printf("%d\n", r);    }    return 0;}

E 宝物

这道题目是最短路的一个变形,我们从入口开始,对每个点进行用最短路算法进行判断,只是把最短路的条件改为能出来时能携带的最大物品量。本质时保证能联通的最大边权最小的最短路,然后对宝藏进行排序,按顺序取出,如果不能取,就停下来,由于这道题的边数很少,属于稀疏图,我们直接写SPFA就行了,需要注意所有边都是双向边,数组要开3w。dijkstra也可以解决这道题目,大家有兴趣可以自己谢谢看

#include <bits/stdc  .h>using namespace std;struct node{    int v, z, next;} e[30020];int head[8010], vis[8010], max1[8010], b[8010], dis[8010], cnt;int addedge(int a, int b, int v){    e[  cnt].v = b;    e[cnt].z = v;    e[cnt].next = head[a];    head[a] = cnt;}void spfa(int s){    queue<int>q;    q.push(s);    vis[s] = 1;    dis[s] = 0x3f3f3f3f;    while(!q.empty())    {        int d = q.front();        q.pop();        vis[d] = 0;        for(int i = head[d]; i; i = e[i].next)        {            int v = e[i].v;            if(dis[v] < min(dis[d], e[i].z))            {                dis[v] = min(dis[d], e[i].z);                if(vis[v] == 0)                {                    q.push(v);                    vis[v] = 1;                }            }        }    }}int main(){    int n, m, k, w, a, b, c,ans;    while(~scanf("%d%d%d%d", &n, &m, &k, &w))    {        memset(vis, 0, sizeof(vis));        memset(dis, 0, sizeof(dis));        memset(head, 0, sizeof(head));        cnt = 0;        for(int i = 0; i < k; i  )            scanf("%d", &b[i]);        for(int i = 1; i <= m; i  )        {            scanf("%d%d%d", &a, &b, &c);            addedge(a, b, c);            addedge(b, a, c);        }        spfa(1);        for(int i = 0; i < k; i  )            max1[i] = dis[b[i]];         ans = 0;        sort(max1, max1   k);        for(int i = 0; i < k; i  )            if(max1[i] >= (ans 1)*w)                ans  ;        printf("%d\n", ans);    }    return 0;}

F Substring

题意很简单,类似于回文串,只不过这次反转后字串只要在整个字符串中有即可。题目范围很小,我们直接枚举所有字串即可解决问题

#include <bits/stdc  .h>char s[105], a[105];int n, maxn, b, e;int main(){    scanf("%d", &n);    while(n--)    {        maxn = 0;        scanf("%s", s);        int len = strlen(s);        for(int i = 0; i < len; i  )            a[len - i - 1] = s[i];        for(int i = 0; i < len - 1; i  )            for(int j = i   1; j < len; j  )            {                for(int k = 0; k < len - j   i; k  )                {                    int judg = 0;                    for(int p = 0; p < j - i   1; p  )                        if(s[i   p] != a[k   p])                        {                            judg = 1;                            break;                        }                    if(!judg && maxn < j - i   1)                    {                        b = i;                        e = j;                        maxn = j - i   1;                    }                }            }        if(maxn)            for(int i = b; i <= e; i  ) printf("%c", s[i]);        else printf("%c", s[0]);        printf("\n");    }    return 0;}

G BOBSLEDDING

题目大意是我们需要保证在转弯时速度不能超过多少,每秒速度变化最多为1.问整个过程中所能保持的最大速度时多少。我们只需要先假设他一直加速,直到遇到转弯,更新当前最大速度和前面所有需要更新的值即可

#include<bits/stdc  .h>using namespace std;int f[100010];int main(){int a,b,l,n;while(~scanf("%d%d",&l,&n)){memset(f,0,sizeof(f));for(int i=1;i<=n;i  )scanf("%d%d",&a,&b),f[a]=b;f[0]=1;for(int i=1;i<=l;i  ){if(f[i]==0||f[i]>f[i-1] 1)f[i]=f[i-1] 1;else{int q=i;while(f[i] 1<f[i-1])f[i-1]=f[i] 1,i--;}}int ans=0;for(int i=0;i<=l;i  )ans=min(ans,f[i]);printf("%d\n",ans);}return 0;}

H SECRET

题目给了一个加权无向图,从起点道终点共走T次,每条路只能走一次。问如何才能让权值最小的边只能走一次。对于这个题目,我们要求最小的最大权值。对于权值,显然不能枚举得出,所以我们二分权值,对于每一个二分的权值,选择满足条件的边标记为流量为1,标记起点终点用dinic跑一遍最大流。得到的最大流量就是能到达终点的次数。判断是否大于T即可结合二分答案确定最大权值

#include<bits/stdc  .h>using namespace std;#define inf 0x3f3f3f3fint N, P, T;struct Edge{    int u, v, w;} e[40010];int g[220][220], vis[220], d[220];bool bfs(){    memset(vis, 0, sizeof(vis));    vis[1] = 1;    queue<int>q;    q.push(1);    d[1] = 0;    while(!q.empty())    {        int u = q.front();        q.pop();        for(int i = 1; i <= N;   i)        {            if(!vis[i] && g[u][i] > 0)            {                vis[i] = 1;                d[i] = d[u]   1;                q.push(i);            }        }    }    return vis[N];}int dfs(int u, int a){    if(u == N || a == 0) return a;    int flow = 0, f;    for(int i = 1; i <= N;   i)    {        if(g[u][i] && d[i] == d[u]   1 && (f = dfs(i, min(a, g[u][i]))) > 0)        {            g[u][i] -= f;            g[i][u]  = f;            flow  = f;            a -= f;            if(!a) break;        }    }    return flow;}int solve(){    int res = 0;    while(bfs())    {        res  = dfs(1, inf);    }    return res;}bool ok(int mid){    memset(g, 0, sizeof(g));    for(int i = 1; i <= P;   i)    {        if(e[i].w <= mid)        {            g[e[i].u][e[i].v]  ;            g[e[i].v][e[i].u]  ;        }    }    return solve() >= T;}int main(){    while(scanf("%d%d%d", &N, &P, &T) != EOF)    {        for(int i = 1; i <= P;   i) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);        int l = 0, r = 1000010;        while(l < r)        {            int mid = l   ((r - l) >> 1);            if(ok(mid)) r = mid;            else l = mid   1;        }        cout << l << endl;    }    return 0;}

来源:https://www.icode9.com/content-1-846601.html

(0)

相关推荐