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 0x3f3f3f3f
int 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;
}
posted @ 2021-02-05 06:30  徒手拆机甲  阅读(158)  评论(0)    收藏  举报