2018年湘潭大学程序设计竞赛 个人笔记 题解

版权声明:点个关注(^-^)V https://blog.csdn.net/weixin_41793113/article/details/89459665

题目链接:https://ac.nowcoder.com/acm/contest/105?&headNav=www#question

A.时间统计

链接:https://ac.nowcoder.com/acm/contest/105/A
来源:牛客网
 

题目描述

某个实验需要统计时间,记录了实验开始和结束的时间,计算实验用了多少秒。 

输入描述:

第一行输入一个整数n,表示样例个数。接下来每组样例两行,表示开始时间和结束时间,格式为xdayHH:MM:SS,x是一个整数表示第几天,0 < x < 20000,HH表示小时,MM表示分钟,SS表示秒,保证时间合法,结束时间比开始时间晚。

输出描述:

每组数据输出一行一个整数,表示经过的秒数。

示例1

输入

复制

2
1day03:26:12
1day04:26:12
123day15:00:01
19999day15:00:00

输出

复制

3600
1717286399

模拟,一道签到题 

#include<iostream>
#include<cstdio>
using namespace std;

typedef long long ll;

int main(){
    int n,d1,h1,m1,t1,d2,h2,m2,t2;
    ll ans =0;
    scanf("%d",&n);
    while(n--){
        scanf("%dday%d:%d:%d",&d1,&h1,&m1,&t1);
        scanf("%dday%d:%d:%d",&d2,&h2,&m2,&t2);
        ans =0;
        ans=24*3600*(d2-d1)+3600*(h2-h1)+60*(m2-m1)+t2-t1;
        printf("%lld\n",ans);
    }

	return 0;
}

B.String

链接:https://ac.nowcoder.com/acm/contest/105/B
来源:牛客网
 

题目描述

有一个只包含大写字母和数字的字符串S,和一个6*6的字符组成的正方形如下图,正方形中恰好包含0-9和A-Z各一个字符。正方形的一行在字符串S中的出现次数是这行6个字符在S中出现次数的总和,正方形的一列在字符串S中的出现次数是这列6个字符在S中出现次数的总和。如果正方形中一个字符所在行的出现次数是所有行中最多,并且所在列的出现次数是所有列中最多,这个字符是需要被找出的。

012345
6789AB
CDEFGH
IJKLMN
OPQRST
UVWXYZ

输入描述:

第一行是一个整数T(1 ≤ T ≤ 400),表示样例的个数。
每个样例一行字符串S,长度1≤ len ≤ 500。

输出描述:

每组数据输出一行需要找出的所有字符,如果需要找出的字符有多个,所在行不同的按行从小到大排序,所在行相同的按列从小到大排序。

示例1

输入

复制

3
13579
AADD
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ

输出

复制

13
7ADG
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ

给出一个标准的矩阵,现在有一个字符串S,对于标准矩阵来说,一行(或者一列)的权值为这一行(或列)中与S中相同字符的个数(不是种类数)。现在要输出最大权值的行和列的交点的字符。

这题难点就读题,匹配不会超时乱搞吧。

#include<iostream>
#include<string>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
#include<queue>
#include<set>
#include<cstdio>
#include<functional>
#include<iomanip>
#include<cmath>
#include<stack>
#include<iomanip>
#include<functional>
using namespace std;
const int maxn = (int)(1e5) + 10;
const int BN = 30;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-6;
typedef long long LL;
typedef unsigned long long ull;
char ori[10][10] = {
	"012345","6789AB","CDEFGH",
	"IJKLMN","OPQRST","UVWXYZ"
};
int ccol[10], crow[10];
char s[550];
int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%s", s);
		int len = strlen(s), maxc = 0, maxr = 0;
		memset(ccol, 0, sizeof(ccol));
		memset(crow, 0, sizeof(crow));
		for (int i = 0; i < 6; i++) {
			for (int j = 0; j < 6; j++) {
				for (int k = 0; k < len; k++) {
					if (ori[i][j] == s[k]) {
						ccol[i]++;
						crow[j]++;
						maxc = max(maxc, ccol[i]);
						maxr = max(maxr, crow[j]);
					}
				}
			}
		}
		string ans;
		for (int i = 0; i < 6; i++) {
			for (int j = 0; j < 6; j++) {
				if (ccol[i] == maxc&&crow[j] == maxr) {
					ans.push_back(ori[i][j]);
				}
			}
		}
		cout << ans << endl;
	}
	return 0;
}

C.Boom

链接:https://ac.nowcoder.com/acm/contest/105/C
来源:牛客网
 

题目描述

紧急事件!战场内被敌军埋放了n枚炸弹!
我军情报部门通过技术手段,掌握了这些炸弹的信息。这些炸弹很特殊,每枚炸弹的波及区域是一个矩形。第i枚炸弹的波及区域是以点(xi1,yi1)为左下角,点(xi2,yi2)为右上角的矩形。
mostshy,作为我军的指挥官,想要知道,如果这些弹同时被敌军引爆,最多将有多少枚炸弹会波及到同一个区域(不考虑边界和角落)。

输入描述:

第一行是一个整数T(1 ≤ T ≤ 50),表示样例的个数。
以后每个样例第一行是一个整数n(1 ≤ n ≤ 50),表示炸弹的个数。
接下来n行,每行四个整数,第i行为xi1,yi1,xi2,yi2(0 ≤ xi1,yi1,xi2,yi2≤ 100),输入保证合法。

输出描述:

每个样例输出一行,一个整数,表示最多将有多少枚炸弹会波及到同一个区域。

示例1

输入

复制

1
2
0 0 50 50
40 40 100 100

输出

复制

2

说明

在左下角为(40,40),右上角为(50,50)的矩形区域内,有两个炸弹同时波及,所以答案为2。

因为数据量很小,可以直接对这个坐标化为100*100的格子,进行标记。

这题会卡边界,所以要严格左闭右开

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int a[105][105];
int n;
int x1,y1,x2,y2;

int main(){
    int T;
    scanf("%d",&T);

    while(T--){
        scanf("%d",&n);
        memset(a,0,sizeof(a));
        for(int k=1;k<=n;k++){
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);

            for(int i=x1;i<x2;i++)
                for(int j=y1;j<y2;j++)
                    a[i][j]++;
        }
        int ans=0;
        for(int i=0;i<=100;i++)
            for(int j=0;j<=100;j++)
                ans = max(ans,a[i][j]);
        printf("%d\n",ans);
    }

	return 0;
}

D.Fibonacci进制 

链接:https://ac.nowcoder.com/acm/contest/105/D
来源:牛客网
 

题目描述

Fibonacci数是非常有名的一个数列,它的公式为 f(n)=f(n-1)+f(n-2),f(0)=1,f(1)=2。 

我们可以把任意一个数x表示成若干不相同的Fibonacci数的和, 比如说14 = 13+1 = 8+5+1 = 8+3+2+1。

如果把Fibonacci数列作为数的位权,即f(i)作为第i位的位权,每位的系数只能是0或者1,从而得到一个01串。 比如14可以表示成 100001,11001,10111。 我们再把这个01串看成2进制,再转成10进制以后就变成了 33,25,23。 为了避免歧义,我们将使用最小的那个值23。

 请按照这个过程计算一下10进制整数通过上述转换过程得到的10进制整数。

输入描述:

第一行是一个整数T(1 ≤ T ≤ 10000),表示样例的个数。
以后每行一个样例,为一个十进制正整数x(1 ≤ x ≤ 109)。

输出描述:

每行输出一个样例的结果。

示例1

输入

复制

5
1
10
100
1000
1000000000

输出

复制

1
14
367
10966
4083305275263

把一个十进制数转化为斐波那契数(不能重复)的和,然后对这个根据这个些斐波那契数是第几项而转化为01串,这里方便表示为斐波那契进制数。

再把这个斐波那契进制数按二进制转化为十进制。要求输出最小答案。

任何一个实数都可以转化为斐波那契数和,所以贪心的从最逼近这个数的斐波那契数分解起一定能得到一个最大的二进制数。

然后就是怎么对这个01串缩小。

由斐波那契数的性质可知fab[i]=fab[i-1]+fab[i-2]。这样分解可以从最高位开始分解使这个二进制数变小,直到不能分解为止。

然而如果只分解一次是不够的,因为低位的分解之后可能产生空位,使高位的也能分解。(不清楚的读者可以看一下样例

这里读者可以考虑用dfs分解,或者直接分解个20次。

思维,模拟,斐波那契的第n项可以分解为n-1和n-2项,2^{n}>2^{n-1}+2^{n-2},所以是可以放小的,那么我们可以从高位到低位枚举放小,从样例14,100001,11001,10111,可以看出第n位为1,第n-1和n-2位都为0是可以分解的,而且10100这个样例只分解一趟是不行的,因为当低位分解后高位的1也满足条件了

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;

typedef long long ll;
ll F[55];//46超出int
int a[55];

int main(){
    F[0]=F[1]=1;
    for(int i=2;i<=50;i++)
        F[i] = F[i-1] + F[i-2];
    int T;
    ll x;
    scanf("%d",&T);
    while(T--){
        scanf("%lld",&x);
        memset(a,0,sizeof(a));
        for(int i=45;i>=1;i--)
            if(x>=F[i]){
                x-=F[i];
                a[i]=1;
                if(x==0)
                    break;
            }
        while(1){
            int flag = 1;
            for(int i=45;i>=3;i--)
                if(a[i]==1 && a[i-1]==0 && a[i-2]==0){
                    a[i]=0;
                    a[i-1]=a[i-2]=1;
                    flag=0;
                }
            if(flag)
                break;
        }

        ll ans=0;
//        for(int i=45;i>=1;i--)
//            if(a[i]==1) cout<<1;
//            else cout<<0;
//        cout<<endl;
        for(int i=1;i<=45;i++)
            if(a[i]>0)
                ans += pow(2,i-1);
        printf("%lld\n",ans);
    }



	return 0;
}

E.吃货

链接:https://ac.nowcoder.com/acm/contest/105/E
来源:牛客网
 

题目描述

作为一个标准的吃货,mostshy又打算去联建商业街觅食了。
混迹于商业街已久,mostshy已经知道了商业街的所有美食与其价格,而且他给每种美食都赋予了一个美味度,美味度越高表示他越喜爱这种美食。
mostshy想知道,假如带t元去商业街,只能吃一种食物,能够品味到的美食的美味度最高是多少?

输入描述:

第一行是一个整数T(1 ≤ T ≤ 10),表示样例的个数。
以后每个样例第一行是两个整数n,m(1 ≤ n,m ≤ 30000),表示美食的种类数与查询的次数。
接下来n行,每行两个整数分别表示第i种美食的价格与美味度di,ci (1 ≤ di,ci ≤ 109)。
接下来m行,每行一个整数表示mostshy带t(1 ≤ t ≤ 109)元去商业街觅食。

输出描述:

每个查询输出一行,一个整数,表示带t元去商业街能够品味到美食的最高美味度是多少,如果不存在这样的美食,输出0。

示例1

输入

复制

1
3 3
1 100
10 1000
1000000000 1001
9
10
1000000000

输出

复制

100
1000
1001

说明

大量的输入输出,请使用C风格的输入输出。

贪心,二分,前缀

这题有个地方注意的就是价格越高不一定美味度越高,但我们也清楚,价格高,一般价值也会较高,所以我们可以先对结构体的价格排一波序,然后再处理刚刚的那个问题 "价格越高不一定美味度越高",这里可以用前缀处理,node[i].y表示为前i元价格内最高的美味度,这样就转化为了价格高对应美味度高,可以用二分找出符合的价格,这里我用的是二分答案,也可以用c++的upper_bound函数

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

int T,n,m,x,y;
struct Node{
    int x,y;
    bool operator < (const Node &e) const{
        return x<e.x;//小
    }
};

Node node[30005];


int findpos(int x){
    int l=1,r=n,ans=1;
    while(l<=r){
        int mid = l+(r-l)/2;
        if(node[mid].x<=x){
            ans=mid;
            l=mid+1;
        }
        else
            r = mid-1;
    }
    return ans;
}

int main(){
    scanf("%d",&T);

    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&x,&y);
            node[i].x = x;
            node[i].y = y;
        }
        sort(node+1,node+n+1);
        for(int i=2;i<=n;i++)
            node[i].y = max(node[i].y,node[i-1].y);

        while(m--){
            scanf("%d",&x);
            y = findpos(x);
            if(x<node[1].x)
                printf("0\n");
            else
                printf("%d\n",node[y].y);
        }

    }

	return 0;
}

链接:https://ac.nowcoder.com/acm/contest/105/F
来源:牛客网
 

题目描述

小明来到一个由n x m个格子组成的迷宫,有些格子是陷阱,用'#'表示,小明进入陷阱就会死亡,'.'表示没有陷阱。小明所在的位置用'S'表示,目的地用'T'表示。

小明只能向上下左右相邻的格子移动,每移动一次花费1秒。

有q个单向传送阵,每个传送阵各有一个入口和一个出口,入口和出口都在迷宫的格子里,当走到或被传送到一个有传送阵入口的格子时,小明可以选择是否开启传送阵。如果开启传送阵,小明就会被传送到出口对应的格子里,这个过程会花费3秒;如果不开启传送阵,将不会发生任何事情,小明可以继续向上下左右四个方向移动。

一个格子可能既有多个入口,又有多个出口,小明可以选择任意一个入口开启传送阵。使用传送阵是非常危险的,因为有的传送阵的出口在陷阱里,如果小明使用这样的传送阵,那他就会死亡。也有一些传送阵的入口在陷阱里,这样的传送阵是没有用的,因为小明不能活着进入。请告诉小明活着到达目的地的最短时间。

输入描述:

有多组数据。对于每组数据:
第一行有三个整数n,m,q(2≤ n,m≤300,0≤ q ≤ 1000)。
接下来是一个n行m列的矩阵,表示迷宫。
最后q行,每行四个整数x1,y1,x2,y2(0≤ x1,x2< n,0≤ y1,y2< m),表示一个传送阵的入口在x1行y1列,出口在x2行y2列。

输出描述:

如果小明能够活着到达目的地,则输出最短时间,否则输出-1。

示例1

输入

复制

5 5 1
..S..
.....
.###.
.....
..T..
1 2 3 3
5 5 1
..S..
.....
.###.
.....
..T..
3 3 1 2
5 5 1
S.#..
..#..
###..
.....
....T
0 1 0 2
4 4 2
S#.T
.#.#
.#.#
.#.#
0 0 0 3
2 0 2 2

输出

复制

6
8
-1
3

bfs,搜索题毕竟还是搜索题,不是很难,但不知道为什么这题通过率最低qaq,感觉没什么好讲的,要处理的东西比较多,因为传送点可能会有1000个,如果走到每个点在判断它是否可以传送那么复杂度就会很大,所以我们可以预处理,用数组记录每个点可以传送到的点的位置,不能传送就-1,复杂度O(1),同时也很方便的处理了这个传送门的2个出入口是否有陷阱

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;

const int MAX = 305;

struct Node{
    int x,y,t;
    bool operator < (const Node &e) const{
        return t > e.t;//小
    }
};

int n,m,q,x1,y1,x2,y2;
char ch[MAX][MAX];
int vis[MAX][MAX];
int s[MAX][MAX][2];
int dx[4] = {1,0,-1,0};
int dy[4] = {0,1,0,-1};




void bfs(){
    priority_queue<Node> pq;
    pq.push({x1,y1,0});
    if(s[x1][y1][0]!=-1)
        pq.push({s[x1][y1][0],s[x1][y1][1],3});
    while(!pq.empty()){
        Node u = pq.top();
        pq.pop();

        if(u.x==x2 && u.y==y2){
            cout<<u.t<<endl;
            return;
        }
        int sx = s[u.x][u.y][0],sy=s[u.x][u.y][1];
        if(sx!=-1 && vis[sx][sy]>u.t+3){
            pq.push({sx,sy,u.t+3});//传送
            vis[sx][sy] = u.t+3;
        }

        for(int i=0;i<4;i++){
            int x = u.x + dx[i];
            int y = u.y + dy[i];
            if(x>=0 && x<n && y>=0 && y<m && ch[x][y]!='#'){
                if(vis[x][y]>u.t+1){
                    vis[x][y] = u.t+1;
                    pq.push({x,y,u.t+1});
                }
            }
        }

    }

    cout<<-1<<endl;
}

int main(){
    ios::sync_with_stdio(false);
    while(cin>>n>>m>>q){
        memset(vis,999999,sizeof(vis));
        memset(s,-1,sizeof(s));

        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++){
                cin>>ch[i][j];
                if(ch[i][j]=='S')
                    x1=i,y1=j;
                else if(ch[i][j]=='T')
                    x2=i,y2=j;
            }

        for(int i=0;i<q;i++){
            int u,v,uu,vv;
            cin>>u>>v>>uu>>vv;
            if(ch[u][v]!='#' && ch[uu][vv]!='#'){
                s[u][v][0] = uu;
                s[u][v][1] = vv;
            }
        }

        bfs();
    }

	return 0;
}

G.又见斐波那契

链接:https://ac.nowcoder.com/acm/contest/105/G
来源:牛客网
 

题目描述

输入描述:

第一行是一个整数T(1 ≤ T ≤ 1000),表示样例的个数。
以后每个样例一行,是一个整数n(1 ≤ n ≤ 1018)。

输出描述:

每个样例输出一行,一个整数,表示F(n) mod 1000000007。

示例1

输入

复制

4
1
2
3
100

输出

复制

1
16
57
558616258

 

看公式。

显然是矩阵快速幂,和2016沈阳一样,不过这里是i^3。

把初始矩阵弄个出来就行了(填数字游戏)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

typedef long long ll;
const ll mod = 1e9+7;

struct Matrix{
    ll a[6][6];
    Matrix(){memset(a,0,sizeof(a));}
    Matrix operator * (const Matrix & rhs){
        Matrix res;
        for(int i=0;i<6;i++)
          for(int j=0;j<6;j++)
            for(int k=0;k<6;k++)
                res.a[i][j] = (res.a[i][j] + a[i][k]*rhs.a[k][j])%mod;
        return res;
    }
}res,G;


void mpow(ll n){
    while(n>0){
        if(n%2==1)
            res=res*G;
        G=G*G;
        n>>=1;
    }
}


int main(){
    ll T,n;
    scanf("%lld",&T);
        ll b[6][6] =  {{1,1,1,1,1,1},
                       {1,0,0,0,0,0},
                       {0,0,1,3,3,1},
                       {0,0,0,1,2,1},
                       {0,0,0,0,1,1},
                       {0,0,0,0,0,1}};
    while(T--){
        for(int i=0;i<6;i++)
          for(int j=0;j<6;j++){
                G.a[i][j] = b[i][j];
                if(i==j)
                    res.a[i][j] = 1;
                else
                    res.a[i][j] = 0;
          }
        scanf("%lld",&n);
        mpow(n-1);
        ll ans = (res.a[0][0] + 8*res.a[0][2] + 4*res.a[0][3] + 2*res.a[0][4] + res.a[0][5])%mod;
        printf("%lld\n",ans);
    }
	return 0;
}

H.统计颜色

链接:https://ac.nowcoder.com/acm/contest/105/H
来源:牛客网
 

题目描述

n个桶按顺序排列,我们用1~n给桶标号。有两种操作:
1 l r c 区间[l,r]中的每个桶中都放入一个颜色为c的球 (1≤l,r ≤n,l≤r,0≤c≤60)
2 l r   查询区间[l,r]的桶中有多少种不同颜色的球     (1≤l,r ≤n,l≤r)

输入描述:

有多组数据,对于每组数据:
第一行有两个整数n,m(1≤n,m≤100000)
接下来m行,代表m个操作,格式如题目所示。

输出描述:

对于每个2号操作,输出一个整数,表示查询的结果。

示例1

输入

复制

10 10
1 1 2 0
1 3 4 1
2 1 4
1 5 6 2
2 1 6
1 7 8 1
2 3 8
1 8 10 3
2 1 10
2 3 8

输出

复制

2
3
2
4
3

线段树区间更新,用或运算进行状态压缩(c<=60疯狂暗示压缩)

#include<iostream>
#include<cstdio>
using namespace std;

typedef long long ll;

const int MAX = 100005;

struct Node{
    int l,r;
    ll sum,lazy;
}tree[4*MAX];

int n,m,op,x,y;
ll c;

void pushup(int u){
    tree[u].sum = tree[2*u].sum | tree[2*u+1].sum;
}

void pushdown(int u){
    if(tree[u].lazy!=0){
        tree[2*u].lazy = tree[2*u].lazy | tree[u].lazy;
        tree[2*u+1].lazy = tree[2*u+1].lazy | tree[u].lazy;
        tree[2*u].sum = tree[2*u].sum | tree[u].lazy;
        tree[2*u+1].sum = tree[2*u+1].sum | tree[u].lazy;
        tree[u].lazy = 0;
    }
}

void buildTree(int u,int l,int r){
    tree[u].sum=tree[u].lazy=0;
    tree[u].l = l;
    tree[u].r = r;

    if(l==r)
        return;
    int mid = l + (r-l)/2;
    buildTree(2*u,l,mid);
    buildTree(2*u+1,mid+1,r);
}

void update(int u,int l,int r,ll add){
    if(tree[u].l>=l && tree[u].r<=r){
        tree[u].lazy = tree[u].lazy | add;
        tree[u].sum = tree[u].sum | add;
        return;
    }

    pushdown(u);
    int mid = tree[u].l + (tree[u].r-tree[u].l)/2;
    if(l<=mid)
        update(2*u,l,r,add);
    if(r>mid)
        update(2*u+1,l,r,add);
    pushup(u);
}

ll query(int u,int l,int r){
    if(tree[u].l>=l && tree[u].r<=r)
        return tree[u].sum;

    pushdown(u);
    int mid = tree[u].l + (tree[u].r-tree[u].l)/2;
    ll ans = 0;
    if(l<=mid)
        ans = ans | query(2*u,l,r);
    if(r>mid)
        ans = ans | query(2*u+1,l,r);
    return ans;
}

ll getAns(ll x){
    ll ans = 0;
    while(x>0){
        ans += x%2;
        x/=2;
    }
    return ans;
}


int main(){
    while(~scanf("%d%d",&n,&m)){
        buildTree(1,1,n);
        while(m--){
            scanf("%d",&op);
            if(op==1){
                scanf("%d%d%lld",&x,&y,&c);
                ll add = (1ll)<<c;
                update(1,x,y,add);
            }else if(op==2){
                scanf("%d%d",&x,&y);
                ll ans = query(1,x,y);
                printf("%lld\n",getAns(ans));
            }
        }
    }

	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41793113/article/details/89459665