北京大学2017计算机学科夏令营上机考试

文章目录

          • A 计判决素数个数
          • B 编码字符串
          • C 岛屿周长
          • D Safecracker
          • E 怪盗基德的滑翔翼
          • F Full Tank?
          • G 实现堆结构
          • H Subway
          • I C Looooops
          • J Captain Q’s Treasure

参考博文: 【题目自解】北京大学2017计算机学科夏令营上机考试
题目链接: 北京大学2017计算机学科夏令营上机考试

题目名称 题目类型 测试情况
A 计判决素数个数 模拟题 AC
B 编码字符串 模拟题 AC
C 岛屿周长 DFS AC
D Safecracker 暴力枚举/DFS AC
E 怪盗基德的滑翔翼 DP WR
F Full Tank? 图论/BFS
G实现堆结构 优先队列 AC
H Subway 图论
I C Looooops 数论
J Captain Q’s Treasure
A 计判决素数个数

思路:简单题。在输入的x和y的范围内遍历判断是否是素数,累加计数。

代码:

#include 
#include 
#include 
#include 
using namespace std;

bool isPrime(int n)
{
    if(n==1) return 0;
    for(int i=2; i*i<=n; i++)
    {
        if(n%i==0) return 0;
    }
    return 1;
}
int solve(int x, int y)
{
    int sum = 0;
    for(int i=x; i<=y; i++)
    {
        if(isPrime(i)) sum++;
    }
    return sum;
}
int main()
{
    int X, Y;
    cin >> X >> Y;
    if(X>Y) swap(X, Y);
    cout << solve(X, Y) << endl;
    return 0;
}
B 编码字符串

思路:简单模拟题,使用数组累积连续相同的字符,注意输出后需要还原为0。

代码:

#include 
#include 
#include 
#include 
using namespace std;

int a[50];
int main()
{
    string s;
    cin >> s;
    memset(a, 0, sizeof(a));
    int pre = -1, cnt  = -1;
    for(int i=0; i<s.size(); i++)
    {
        pre = cnt;
        if(isupper(s[i]))
            cnt = s[i]-'A';
        else
            cnt = s[i]-'a';
        a[cnt]++;
        if(pre!=-1 && cnt!=pre)
        {
            cout << "(" << char(pre+'a') << "," << a[pre] << ")";
            a[pre] = 0;
        }
    }
    if(s.size()>0)
    {
        cout << "(" << char(pre+'a') << "," << a[pre] << ")";
        a[pre] = 0;
    }
    cout << endl;
    return 0;
}
C 岛屿周长

思路:简单题,深搜遍历上下左右的点,如果是海或者是边界,那么周长++。

代码:

#include 
#include 
#include 
using namespace std;

const int MAX = 105;
int G[MAX][MAX], sum = 0;
int vis[MAX][MAX];
int dx[5] = {0, 0, 1, -1};
int dy[5] = {1, -1, 0, 0};
//dfs遍历小岛
void dfs(int x, int y)
{
    vis[x][y] = 1;
    for(int i=0; i<4; i++)
    {
        int x1 = x+dx[i], y1 = y+dy[i];
        if(G[x1][y1]==0)
        {
            sum++;
        }
        else if(!vis[x1][y1])
        {
            dfs(x1, y1);
        }
    }
}

int main()
{
    int n, m, a;
    cin >> n >> m;
    memset(G, 0, sizeof(G));
    memset(vis, 0, sizeof(vis));
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            cin >> a;
            G[i][j] = a;
        }
    }
    //找到一个子为1的位置
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            if(G[i][j])
            {
                dfs(i, j);
                break;
            }
        }
        if(sum) break;
    }
    cout << sum << endl;
    return 0;
}
D Safecracker

思路:可以暴力枚举(5层循环),也可以使用递归回溯法。

暴力枚举:
代码:

#include 
#include 
#include 
#include 
#include 
using namespace std;

int Pow(int x, int n)
{
    int ans = 1;
    for(int i=0; i<n; i++)
    {
        ans *= x;
    }
    return ans;
}
int main()
{
    int n;
    string s;
    while(cin >> n >> s)
    {
        if(n==0 && s=="END") break;
        int len = s.size(), flag = 0;
        //需要将原字符串的字符排序
        sort(s.begin(), s.end());
        //枚举
        for(int v=len-1; v>=0; v--)
        {
            string t  = "11111";
            int vc = s[v]-'A'+1;
            t[0] = s[v];
            for(int w=len-1; w>=0; w--)
            {
                if(w==v) continue;
                int wc = s[w]-'A'+1;
                t[1] = s[w];
                for(int x=len-1; x>=0; x--)
                {
                    if(x==v || x==w) continue;
                    int xc = s[x]-'A'+1;
                    t[2] = s[x];
                    for(int y=len-1; y>=0; y--)
                    {
                        if(y==v || y==w || y==x) continue;
                        int yc = s[y]-'A'+1;
                        t[3] = s[y];
                        for(int z=len-1; z>=0; z--)
                        {
                            if(z==v || z==w || z==x || z==y) continue;
                            int zc = s[z]-'A'+1;
                            t[4] = s[z];
                            if(vc-Pow(wc, 2)+Pow(xc, 3)-Pow(yc, 4)+Pow(zc, 5)==n)
                            {
                                flag = 1;
                                cout << s[v] << s[w] << s[x] << s[y] << s[z] << endl;
                            }
                            if(flag) break;
                        }
                        if(flag) break;
                    }
                    if(flag) break;
                }
                if(flag) break;
            }
            if(flag) break;
        }
        if(!flag) cout << "no solution" << endl;
    }
    return 0;
}

递归回溯:
代码:

#include 
#include 

using namespace std;

int n;
int len;
char s[15];//存放原字符串
int vis[15];//深搜访问标记
int t[5];//存放最终字符串
int p[5];//转换后的数字

void check()//更新为最大字典序
{
    for (int i = 0; i < 5; i++)
    {
        if (p[i] > t[i])
        {
            for (int j = 0; j < 5; j++)
            {
                t[j] = p[j];
            }
            break;
        }
        if (p[i] < t[i])
        {
            break;
        }
    }
}

void dfs(int cur)
{
    if (cur == 5)
    {
        if (n == p[0] - p[1] * p[1] + p[2] * p[2] * p[2] - p[3] * p[3] * p[3] * p[3] + p[4] * p[4] * p[4] * p[4] * p[4])
        {
            check();
        }
    }
    else
    {
        for (int i = 0; i < len; i++)//逐元素向前推进进行深搜
        {
            if (!vis[i])
            {
                vis[i] = 1;
                p[cur] = s[i] - 'A' + 1;
                dfs(cur + 1);
                vis[i] = 0;//回溯
            }
        }
    }
}

int main()
{
    while (true)
    {
        cin >> n >> s;
        if (n == 0)
        {
            return 0;
        }
        len = strlen(s);
        memset(vis, 0, sizeof(vis));
        memset(t, 0, sizeof(t));
        memset(p, 0, sizeof(p));
        dfs(0);
        if (t[0] == 0)
        {
            cout << "no solution" << endl;
        }
        else
        {
            for (int i = 0; i < 5; i++)
            {
                cout << (char)(t[i] + 'A' - 1);
            }
            cout << endl;
        }
    }
    return 0;
}
E 怪盗基德的滑翔翼

思路:DP简单题。之前很久没有做DP了,居然忘记了LIS的做法,这个就是正向和反向两次LIS,然后求出最大,注意需要分别使用两个数组进行LIS。

代码:

#include 
#include 
#include 
using namespace std;

int L1[105], L2[105], a[105];

int main()
{
    int n, m;
    cin >> n;
    while(n--)
    {
        cin >> m;
        fill(L1, L1+m+1, 1);
        fill(L2, L2+m+1, 1);
        //正向LIS
        for(int i=0; i<m; i++)
        {
            cin >> a[i];
            for(int j=i-1; j>=0; j--)
            {
                if(a[j]<=a[i])
                    L1[i] = max(L1[j]+1, L1[i]);
            }
        }
        //反向LIS
        for(int i=m-1; i>=0; i--)
        {
            for(int j=i+1; j<m; j++)
            {
                if(a[j]<=a[i])
                    L2[i] = max(L2[j]+1, L2[i]);
            }
        }
        //求出最大的值
        int Max = 0;
        for(int i=0; i<m; i++)
        {
            if(Max<L1[i]) Max = L1[i];
            if(Max<L2[i]) Max = L2[i];
        }
        cout << Max << endl;
    }
    return 0;
}
F Full Tank?

思路:可以使用BFS+优先队列的方法这个问题。首先定义状态Node(u, vol,cost)分别为结点编号,剩余油量,已经花费的钱。其中u和vol可以区别两个状态,cost用于记录值。思路是每一次选出cost最小的状态,给它加入一个单位的油(不超过容量),然后将新状态入队列,同时检查是否可以到达另一个结点,如果可以则该节点状态入队列。
1.每一次加一升油(因为题目的条件这些都是整数,所以可以类似离散化处理,一点一点加入油)
2.如果加的油足够走到下一个结点,那就走到下一个结点吧(记得减去路上消耗的油,还有花费不变…)
(至于在第二次扩展时要不要加油,这个就不需要了.因为在第一种情况扩展结点的时候加油了…)

代码:

#include
#include
#include
#include
#include
using namespace std;

const int Max = 10000+5;

struct Node{
	int u, vol, cost;//u是结点编号,vol是当前结点是所剩的油量,cost是当前的总花费
	Node(int a=-1, int b=-1, int c=-1):u(a), vol(b), cost(c){}
	bool operator <(const Node &a) const{
		return cost > a.cost;//优先队列中,最小值优先
	}
};
struct Road
{
    int to, v;//v是公路长度
    Road(int to=-1, int v=-1):to(to), v(v){}
    bool operator < (const Road &A) const
    {
        if(v==A.v) return to<A.to;
        else       return v<A.v;
    }
};
vector<Road> G[Max];
bool vis[Max][105];
int price[Max];
int n, m;

void bfs(int VOL, int s, int e)
{
    priority_queue<Node> q;
    memset(vis, 0, sizeof(vis));
    q.push(Node(s, 0, 0));
    vis[s][0] = 1;
    while(!q.empty())
    {
        Node v = q.top(); q.pop();
        int u = v.u, vol = v.vol, c = v.cost;
        if(u==e)
        {
            printf("%d\n", c);
            return;
        }
        //在剩余油量不满的情况下,当该状态没有遍历过,则加入该状态
        if(vol<VOL && !vis[u][vol+1])
        {
            q.push(Node(u, vol+1, c+price[u]));
        }
        //判断是否可以到达其他结点,加入状态
        for(int i=0; i<G[u].size(); i++)
        {
            int to = G[u][i].to, w = G[u][i].v;
            if(vol>=w && !vis[to][vol-w])
            {
                vis[to][vol-w] = 1;
                q.push(Node(to, vol-w, c));
            }
        }
    }
    printf("impossible\n");
}

int main()
{
	while(scanf("%d%d", &n, &m)!=EOF){
		int u, v, x, q;
		for(int i=0; i<n; i++) {
			scanf("%d", &price[i]);
			G[i].clear();
		}

        while(m--){
            scanf("%d%d%d", &u, &v, &x);
            G[u].push_back(Road(v, x));
            G[v].push_back(Road(u, x));
        }

        scanf("%d", &q);
        while(q--){
            scanf("%d%d%d", &x, &u, &v);
            bfs(x, u, v);
        }
	}
	return 0;
 }
G 实现堆结构

思路:需要知道优先队列是使用堆结构实现的(默认是大顶堆),然后就是优先队列的裸题。

代码:

#include 
#include 
#include 
#include 
using namespace std;

priority_queue<int, vector<int>, greater<int> >q;
int main()
{
    int n, t, u;
    cin >> n;
    while(n--)
    {
        cin >> t;
        if(t==1)
        {
            cin >> u;
            q.push(u);
        }
        else
        {
            int v = q.top();
            q.pop();
            cout << v << endl;
        }
    }
    return 0;
}
H Subway

思路:本题难点在与构建图。结点实际上是地铁站点和起始点、目的点,任意两个点之间均可以有边,边的权值是从一个点直接到达另一个点的时间,所以只有相邻的地铁站点之间可以使用地铁速度,其他点之间只能使用步行速度。建好图之后就是最短路问题。

代码:


#include 
#include
#include
#include
#include
#include
#include
#include
#include 
#include
#include
#include 
#define MAX_N 1000000
#define INF 0x3f3f3f3f
#define SIZE 1000
#define pai 3.14159265358979323
#define atm 1000000007
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
 
struct edge
{
    int x;
    int y;
}p[MAX_N];

int n;
double G[500][1000];
double walk(int a, int b)
{
    double m = sqrt((double)(p[a].x - p[b].x) * (p[a].x - p[b].x) + (double)(p[a].y - p[b].y) * (p[a].y - p[b].y));
    return m * 3 / 500;
}
double subwey(int a, int b)
{
    double m = sqrt((double)(p[a].x - p[b].x) * (p[a].x - p[b].x) + (double)(p[a].y - p[b].y) * (p[a].y - p[b].y));
    return m * 3 / 2000;
}
//Floyd算法
void solve()
{
    for (int k = 1; k <= n; k++)
    {
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                G[i][j] = min(G[i][j], G[i][k] + G[k][j]);
            }
        }
    }
}
int main()
{
    int a, b;
    int j = 0;
    n = 3;
    scanf("%d%d%d%d", &p[1].x, &p[1].y, &p[2].x, &p[2].y);
    G[1][2] = G[2][1] = walk(1, 2);
    while (scanf("%d%d", &a, &b) != EOF)
    {
        if (a == -1 && b == -1)
            j = 0;
        else
        {
            p[n].x = a;
            p[n].y = b;
            //不相邻的站点之间的直接距离看做是步行,相邻的站点可以坐地铁
            for (int i = 1; i < n - j; i++)
            {
                G[i][n] = G[n][i] = walk(i, n);
            }
            for (int i = n - j; i < n; i++)
            {
                G[i][n] = G[n][i] = subwey(i, n);
            }
            j = 1;
            n++;
        }
    }
    n--;
    solve();
    printf("%.0f\n", G[1][2]);
    return 0;
}
I C Looooops
J Captain Q’s Treasure

你可能感兴趣的:(ACM刷题之路)