2015 ZJBTI校赛题解

本渣渣很荣幸出了这次校赛的题,题目相对都比较简单,没有任何算法题,都是暴力可以过的(除了F题需要些技巧,不过都是常用的技巧)。

先放一个所有题目的链接: 2015 ZJBTI

============================================================

【A(1115)题】

这题没什么好说吧,就为了保证能送出U盘,直接上代码

C++:

#include <cstdio>
using namespace std;

int main()
{
#ifdef SHY
    freopen("e:\\1.txt", "r", stdin);
#endif
    int t;
    scanf("%d%*c", &t);
    while(t--)
    {
        int a,b;
        scanf("%d %d%*c", &a, &b);
        if(a >= b) printf("%d\n", a-b);
        else printf("0\n");
    }
    return 0;
}

Java

import java.io.*;
import java.util.*;

public class Main
{
    public static void main(String args[]) throws Exception
    {
            Scanner in=new Scanner(System.in);
            int t = in.nextInt();
            for(int i = 0; i < t; i++)
            {
                int a = in.nextInt(),b = in.nextInt();
                if(a >= b) System.out.println(a-b);
                else System.out.println("0");
            }
    }

}

 

============================================================

【B(1116)题】

可以直接模拟双方轮流攻击,先算好双方攻击对方的伤害(就是A题的求法),然后记录下双方生命值,模拟轮流攻击,谁先死就输了,代码:

C++

#include <cstdio>
using namespace std;
int hp[2], s[2];//hp[]表示双方生命,s[]表示双方攻击对方造成的伤害

bool ok()//模拟计算谁的生命值先<=0就输了
{
    int a = 0;
    while(hp[0] > 0 && hp[1] > 0)
    {
        hp[a^1] -= s[a];
        a ^= 1;//a^1就是轮流攻击,不会^的可以百度学习下位运算,或者你可以用其他方法替换掉,自己想
    }
    if(hp[0] <= 0) return false;
    return true;
}
int main()
{
#ifdef SHY
    freopen("e:\\1.txt", "r", stdin);
#endif
    int t;
    scanf("%d%*c", &t);
    while(t--)
    {
        int p[2],d[2];
        for(int i = 0;i < 2; i++) scanf("%d %d %d%*c", &p[i],&d[i],&hp[i]);
        for(int i = 0;i < 2; i++) s[i] = p[i]>=d[i^1]?p[i]-d[i^1]:0;
        if(ok()) puts("victory");
        else puts("failure");
    }
    return 0;
}

其实还有一种想当简单的和快速的方法:不难发现,只要算出我方杀死敌人需要攻击几次t[0]以及敌人杀死我方需要攻击几次t[1],如果t[0]<=t[1]就是我方胜利,为什么是<=呢?因为我方先攻击的,代码如下:

C++:

#include <cstdio>
using namespace std;
const int INF = 0x3f3f3f3f;
int hp[2], s[2];

int main()
{
#ifdef SHY
    freopen("e:\\1.txt", "r", stdin);
    //freopen("e:\\out.txt", "w", stdout);
#endif
    int t;
    scanf("%d%*c", &t);
    while(t--)
    {
        int p[2],d[2],t[2];
        for(int i = 0; i < 2; i++) scanf("%d %d %d%*c", &p[i],&d[i],&hp[i]);
        for(int i = 0; i < 2; i++) s[i] = p[i]>=d[i^1]?p[i]-d[i^1]:0;//算出攻击对方的伤害
        for(int i = 0; i < 2; i++)//算出杀死对方需要攻击几次
        {
            if(s[i])
                t[i] = hp[i^1]/s[i]+bool(hp[i^1]%s[i]);
            else//如果伤害为0就无法杀死对方,题目保证了不会存在两个都为0的情况,所以一定有解
                t[i] = INF;
        }
        if(t[0] <= t[1]) puts("victory");
        else puts("failure");
    }
    return 0;
}

Java:

import java.io.*;
import java.util.*;

public class Main
{
    public static void main(String args[]) throws Exception
    {
            Scanner in=new Scanner(System.in);
            int c = in.nextInt(), INF = 0x3f3f3f3f;
            while(c > 0)
            {
            	int hp[] = new int[2], s[] = new int[2];//hp自己血量,s攻击对方造成的伤害
                int p[] = new int[2], d[] = new int[2], t[] = new int[2];//p力量,d防御,t杀死对方需要攻击几次
                //读入
                for(int i = 0; i < 2; i++)
                {
                	p[i] = in.nextInt();
                	d[i] = in.nextInt();
                	hp[i] = in.nextInt();
                }
                //算出攻击对方的伤害
                for(int i = 0; i < 2; i++) s[i] = p[i]>=d[i^1]?p[i]-d[i^1]:0;//这里的i^1就是对方
                for(int i = 0; i < 2; i++)//算出杀死对方需要攻击几次
                {
                    if(s[i] > 0)
                        t[i] = hp[i^1]/s[i]+((hp[i^1]%s[i])>0?1:0);
                    else//如果伤害为0就无法杀死对方,题目保证了不会存在两个都为0的情况,所以一定有解
                        t[i] = INF;
                }
                if(t[0] <= t[1]) System.out.println("victory");
                else System.out.println("failure");
                c--;
            }
    }

}

 

============================================================

【C(1117)题】

用一个sum[]记录每个人拥有的钱,每条消费清单A,B为sum[A] -= B;

算完M条清单后,这时的sum就是最后所有人剩下的钱,扫描一边记录下最大值的编号就可以了

C++:

#include <cstdio>
using namespace std;
const int INF = 0x3f3f3f3f, MAXN = 10000+10;
int sum[MAXN];

int main()
{
#ifdef SHY
	freopen("e:\\1.txt", "r", stdin);
	//freopen("e:\\out.txt", "w", stdout);
#endif
	int t;
	scanf("%d%*c", &t);
	while(t--)
	{
		int n,m;
		scanf("%d %d%*c", &n, &m);
		for(int i = 1; i <= n; i++) scanf("%d%*c", &sum[i]);
		for(int i = 0; i < m; i++)
		{
			int a,b;
			scanf("%d %d%*c", &a, &b);
			sum[a] -= b;
		}
		int mx = -INF,id;
		for(int i = 1; i <= n; i++)
		{
			if(mx < sum[i])
			{
				mx = sum[i];
				id = i;
			}
		}
		printf("%d\n", id);
	}
	return 0;
}

Java:

import java.io.*;
import java.util.*;

public class Main
{
	static int sum[] = new int[10000+10];//表示每个人有多少钱
    public static void main(String args[]) throws Exception
    {
        Scanner in=new Scanner(System.in);
        int t = in.nextInt(), INF = 0x3f3f3f3f;
        while(t-- > 0)
        {
        	int n = in.nextInt(), m = in.nextInt();
        	//初始现金
        	for(int i = 1; i <= n; i++) sum[i] = in.nextInt();
        	//m条消费
        	for(int i = 0; i < m; i++)
        	{
        		int a = in.nextInt(), b = in.nextInt();
        		sum[a] -= b;
        	}
        	int mx = -INF,id = 1;//统计最大值和最大值编号
        	for(int i = 1; i <= n; i++)
        	{
        		if(mx < sum[i])
        		{
        			mx = sum[i];
        			id = i;
        		}
        	}
        	System.out.println(id);
        }
    }
}

 

============================================================

【D(1118)题】

就是每读一行字符串,倒序输出就好了,注意下数字后面的’\n’就行了(本来题目中没提示,怕错太多所以写出来了)

C++:

#include <cstdio>
using namespace std;
const int MAXN = 1000+10;
char a[MAXN];

int main()
{
#ifdef SHY
    freopen("e:\\1.txt", "r", stdin);
    //freopen("e:\\out.txt", "w", stdout);
#endif
    int t;
    scanf("%d%*c", &t);
    while(t--)
    {
        int n,m;
        scanf("%d %d%*c", &n, &m);
        for(int i = 0; i < n; i++)
        {
            gets(a);
            for(int j = m-1; j >= 0; j--) putchar(a[j]);
            putchar('\n');
        }
    }
    return 0;
}

Java:

import java.io.*;
import java.util.*;

public class Main
{
    public static void main(String args[]) throws Exception
    {
        Scanner in=new Scanner(System.in);
        int t = in.nextInt();
        while(t-- > 0)
        {
        	int n = in.nextInt(), m = in.nextInt();
        	in.nextLine();//注意读掉整数后面的一个'\n'符号
        	for(int i = 0; i < n; i++)
        	{
        		String a = in.nextLine();
        		for(int j = m-1; j >= 0; j--) System.out.print(a.charAt(j));
        		System.out.println();
        	}
        }
    }
}

 

============================================================

【E(1119)题】

这题应该是线段树的区间更新区间查询的标准题,但是为了简化这里的数据量n*m的暴力都可以过:

用一个sum[]记录当前站点有多少人则:

对于ADD  a b:就是sum[a~b-1]都加1;

对于DEL a b:就是sum[a~b-1]都减1;

对于QUERY a b:就是求出sum[a~b-1]中的最大值就是实际售出的车票,想想是不是这样

这样n<=10000,m<=1000,最坏的时间复杂度是n*m=10^7,可以1秒左右跑完;

c++暴力代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 10000+10;
int sum[MAXN];

/*对应三种操作*/
void add(int x, int y)
{
    for(int i = x; i <= y; i++) sum[i]++;
}
int query(int x, int y)
{
    int mx = 0;
    for(int i = x; i <= y; i++) mx = max(mx,sum[i]);
    return mx;
}
void del(int x, int y)
{
    for(int i = x; i <= y; i++) sum[i]--;
}
int main()
{
#ifdef SHY
    freopen("e:\\1.txt", "r", stdin);
    //freopen("e:\\out.txt", "w", stdout);
#endif
    int t,count = 0;
    scanf("%d%*c", &t);
    while(t--)
    {
        int n,m;
        scanf("%d %d", &n, &m);
        memset(sum,0,sizeof(sum));
        char op[10];
        int a,b;
        printf("Motorcar#%d\n", ++count);
        for(int i = 0; i < m; i++)
        {
            scanf("%s %d %d", op, &a, &b);
            b--;//保证区间[a,b-1]
            if(!strcmp(op,"ADD")) add(a,b);
            else if(!strcmp(op,"QUERY")) printf("%d\n", query(a,b));
            else del(a,b);
        }
    }
    return 0;
}

JAVA暴力代码:

import java.io.*;
import java.util.*;

public class Main
{
    static int sum[] = new int[10000+10];
    public static void add(int a, int b){
        for(int i = a; i <= b; i++) sum[i]++;
    }
    public static void del(int a, int b){
        for(int i = a; i <= b; i++) sum[i]--;
    }
    public static int query(int a, int b){
        int mx = -1;
        for(int i = a; i <= b; i++){
            if(mx < sum[i])
                mx = sum[i];
        }
        return mx;
    }
    public static void main(String args[]) throws Exception
    {
            Scanner in=new Scanner(System.in);
            int t = in.nextInt();
            int count = 0;
            String op;
            for(int i = 0; i < t; i++){
                int n = in.nextInt(), m = in.nextInt();
                for(int j = 1;j <= n; j++) sum[j] = 0;
                System.out.println("Motorcar#"+(++count));
                for(int j = 0; j < m; j++){
                    op = in.next();
                    int x = in.nextInt(), y = in.nextInt();
                    if(op.charAt(0) == 'A'){
                        add(x,y-1);
                    }
                    else if(op.charAt(0) == 'Q'){
                        System.out.println(query(x,y-1));
                    }
                    else{
                        del(x,y-1);
                    }
                }
            }

    }

}

 

而标准的算法应该是用线段树n*log(n)的时间复杂度,在这里是10^5级别的,显然快很多,我只要把m加大上面的算法马上就超时了,不会线段树的可以去学习下这个数据结构(我这里线段树的写法可能和网上的有很大不同,这个用了剩内存的技巧):

C++:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define id(x,y) ((x)+(y)|(x)!=(y))
const int MAXN = 10000+10;
int mx[MAXN<<1], add[MAXN<<1];//add[]是lazy标记

void push_up(int x, int y, int m)
{
	mx[id(x,y)] = max(mx[id(x,m)],mx[id(m+1,y)]);
}
void push_down(int x, int y, int m)
{
	int u = id(x,y), lc = id(x,m), rc = id(m+1,y);
	if(add[u])
	{
		add[lc] += add[u];
		mx[lc] += add[u];
		add[rc] += add[u];
		mx[rc] += add[u];
		add[u] = 0;
	}
}
int ql, qr, v;
void update(int x, int y)
{
	if(ql <= x && y <= qr)
	{
		mx[id(x,y)] += v;
		add[id(x,y)] += v;
		return;
	}
	int m = (x+y)>>1;
	push_down(x,y,m);
	if(ql <= m) update(x,m);
	if(qr > m) update(m+1,y);
	push_up(x,y,m);
}
int query(int x, int y)
{
	if(ql <= x && y <= qr)
		return mx[id(x,y)];
	int m = (x+y)>>1, ans = -1;
	push_down(x,y,m);
	if(ql <= m) ans = max(ans,query(x,m));
	if(qr > m) ans = max(ans,query(m+1,y));
	return ans;
}
int main()
{
#ifdef SHY
	freopen("e:\\1.txt", "r", stdin);
#endif
	int t, count = 0;
	scanf("%d%*c", &t);
	while(t--)
	{
		int n,m;
		scanf("%d %d%*c", &n, &m);
		n--;
		char op[10];
		memset(mx,0,sizeof(mx));
		memset(add,0,sizeof(add));
		printf("Motorcar#%d\n",++count);
		for(int i = 0; i < m; i++)
		{
			scanf("%s %d %d%*c", op, &ql, &qr);
			qr--;
			if('A' == op[0])
			{
				v = 1;
				update(1,n);
			}
			else if('Q' == op[0]) printf("%d\n", query(1,n));
			else
			{
				v = -1;
				update(1,n);
			}
		}
	}
	return 0;
}

 

============================================================

【F(1120)】

这个是相对来说最难一点的题,其实也很简单,只要用邻接表存图(邻接矩阵会超内存)来存人际关系。这个可以C++ STL里面的vector,或者任何语言通用的链式前向星存图的方法(下面用JAVA实现的)

然后是空间关系,如果每次都把两个人之间的所有距离之和加起来计算的会超时,因为复杂度变成n^2了,而N是10^5,所以不能这么算,其实只要用sum[]存从左到右的前缀和(即sum[i] = sum[i-1]+d[i]),则这样的话计算任意两个人A,B之间距离只要计算sum[B]-sum[A-1]就可以啦,复杂度只要0(1),再乘上N,复杂度还是N。

最后还有个问题就是排序必须使用快排等n*log(n)的算法,不然铁定超时,最简单的当然是用C/C++中的sort()或者qsort()函数,或者JAVA里面的Arrays.sort();

C++版:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 100000+10;
vector<int> g[MAXN];
int sum[MAXN], dis[MAXN];

int main()
{
#ifdef SHY
	freopen("e:\\1.txt", "r", stdin);
	//freopen("e:\\out.txt", "w", stdout);
#endif
	int t;
	scanf("%d%*c", &t);
	sum[0] = 0;
	while(t--)
	{
		int n,m,p,k, d;
		scanf("%d %d %d %d%*c", &n, &m, &p, &k);
		for(int i = 1; i < n; i++) scanf("%d%*c", &d), sum[i] = sum[i-1]+d;
		for(int i = 1; i <= n; i++) g[i].clear();
		while(m--)
		{
			int a,b;
			scanf("%d %d%*c", &a, &b);
			g[a].push_back(b);
			g[b].push_back(a);
		}
		int cnt = 0;
		for(int i = 0; i < g[p].size(); i++)
		{
			int v = g[p][i];
			if(v > p) dis[cnt++] = sum[v-1]-sum[p-1];
			else dis[cnt++] = sum[p-1]-sum[v-1];
		}
		sort(dis,dis+cnt);
		if(cnt < k) puts("-1");
		else printf("%d\n", dis[k-1]);
	}
	return 0;
}

JAVA链式前向星版:

import java.io.*;
import java.util.*;

public class Main
{
    static int MAXN = 200000+10;
    static int sum[] = new int[MAXN];
    static int d[] = new int[MAXN];
    static int head[] = new int[MAXN];//链式前向星
    static int next[] = new int[MAXN];//链式前向星
    static int to[] = new int[MAXN];//链式前向星
    static int ans[] = new int[MAXN];
    static int tol;//链式前向星
    public static void add_edge(int u, int v){//链式前向星
        next[tol] = head[u];
        to[tol] = v;
        head[u] = tol++;
    }
    public static void main(String args[]) throws Exception
    {
            Scanner in=new Scanner(System.in);
            int t = in.nextInt();
            sum[0] = 0;
            for(int i = 0; i < t; i++)
            {
                int n = in.nextInt(),m = in.nextInt(),p = in.nextInt(),k = in.nextInt();
                for(int j = 1; j < n; j++)
                {
                    d[j] = in.nextInt();
                    sum[j] = d[j]+sum[j-1];
                }
                tol = 0;
                for(int j = 1; j <= n; j++){
                    head[j] = -1;
                }
                for(int j = 1; j <= m; j++)
                {
                    int a = in.nextInt(),b = in.nextInt();
                    add_edge(a,b);
                    add_edge(b,a);
                }
                int cnt = 0;
                for(int j = head[p]; -1 != j; j = next[j]){
                    int v = to[j];
                    if(v < p) ans[cnt++] = sum[p-1]-sum[v-1];
                    else ans[cnt++] = sum[v-1]-sum[p-1];
                }
                Arrays.sort(ans,0,cnt);
                if(cnt < k) System.out.println("-1");
                else System.out.println(ans[k-1]);
            }

    }

}

 

============================================================

【G(1121)题】

题意是十佳歌手比赛,有N个评委给出N个成绩,选手得分为去掉一个最高分和一个最低分后的总分,输出这个总分。

知道题意了应该不用说什么了吧,超级简单,代码:

C++:

#include <cstdio>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;

int main()
{
#ifdef SHY
    freopen("e:\\1.txt", "r", stdin);
    freopen("e:\\out.txt", "w", stdout);
#endif
    int t;
    scanf("%d%*c", &t);
    while(t--)
    {
        int n, mx = -1, mi = INF, sum = 0;
        scanf("%d%*c", &n);
        for(int i = 0; i < n; i++)
        {
            int x;
            scanf("%d%*c", &x);
            mx = max(x, mx);
            mi = min(x,mi);
            sum += x;
        }
        printf("%d\n", sum-mi-mx);
    }
    return 0;
}

Java:

import java.io.*;
import java.util.*;

public class Main
{
    public static void main(String args[]) throws Exception
    {
        Scanner in=new Scanner(System.in);
        int t = in.nextInt(), INF = 0x3f3f3f3f;
        while(t-- > 0)
        {
        	int n = in.nextInt(), mx = -1, mi = INF, sum = 0;
        	for(int i = 0; i < n; i++)
        	{
        		int x = in.nextInt();
        		sum += x;
        		if(mx < x) mx = x;
        		if(mi > x) mi = x;
        	}
        	System.out.println(sum-mx-mi);
        }
    }
}

 

============================================================

【H(1122)题】

题意是给你一篇文章,只有大小写字母还有空格和回车,求出有多少个不同的单词,数据量想当小,可以直接暴力,每次出现一个单词就扫描前面出现过的单词,如果存在则忽略,如果没出现过就存下来,答案计数+1。

但是这里用的是另外一种比较高效的方法,用C++的map来替代上面的循环判重,复杂度是O(n*log(n))的

这里用到了C++的STL容器,不会C++容器的点这里,Java中的容器其实也是类似的,一通百通,当然这题你完全可以不用容器(全部手写,就是代码长点,效率一般比STL高,但复杂度级别是一样的)

代码:

C++:

#include <cstdio>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 1000+10;
char a[MAXN];
map<string, int> vis;

int main()
{
#ifdef SHY
	freopen("e:\\1.txt", "r", stdin);
#endif
	vis.clear();
	while(~scanf("%s", a))
		vis[a] = true;
	printf("%d\n", vis.size());
	return 0;
}

Java:

import java.io.*;
import java.util.*;

public class Main
{
    public static void main(String args[]) throws Exception
    {
        Scanner in=new Scanner(System.in);
        TreeMap<String,Integer> vis = new TreeMap<String,Integer>();
        vis.clear();
        while(in.hasNext()){
        	String a = in.next();
        	vis.put(a,1);
        }
        System.out.println(vis.size());
    }
}

这题还可以用HASH或者字典树来解,都是比较高效的(其实我只放了不到100个单词,每个单词的长度都不超过15,所以随便怎么写都很快)

发表评论

邮箱地址不会被公开。 必填项已用*标注

请输入正确的验证码