本渣渣很荣幸出了这次校赛的题,题目相对都比较简单,没有任何算法题,都是暴力可以过的(除了F题需要些技巧,不过都是常用的技巧)。
先放一个所有题目的链接: 2015 ZJBTI
============================================================
这题没什么好说吧,就为了保证能送出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");
}
}
}
============================================================
可以直接模拟双方轮流攻击,先算好双方攻击对方的伤害(就是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--;
}
}
}
============================================================
用一个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);
}
}
}
============================================================
就是每读一行字符串,倒序输出就好了,注意下数字后面的’\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();
}
}
}
}
============================================================
这题应该是线段树的区间更新区间查询的标准题,但是为了简化这里的数据量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;
}
============================================================
这个是相对来说最难一点的题,其实也很简单,只要用邻接表存图(邻接矩阵会超内存)来存人际关系。这个可以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]);
}
}
}
============================================================
题意是十佳歌手比赛,有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);
}
}
}
============================================================
题意是给你一篇文章,只有大小写字母还有空格和回车,求出有多少个不同的单词,数据量想当小,可以直接暴力,每次出现一个单词就扫描前面出现过的单词,如果存在则忽略,如果没出现过就存下来,答案计数+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,所以随便怎么写都很快)