5.1比赛总结加解题报告

比赛成绩很差很差。。8道题竟然只水出来三道,还有好几道题目也是水题,但是由于英语的原因。没看懂题目意思,然后坑了。英语是硬伤啊,要抓紧时间提升英语水平啊,以后的题目都是全英文的,无奈之。
这次比赛用的题目是codeforces上的第245号比赛题,不过题目的顺序给挪动了。写写解题报告把。。我还是按照codeforces上的顺序来把。

A.System Administrator

题目描述:Polycarpus有两个操作,分别是ping a服务器和pingb服务器,每一次ping都返回一对x,y,且x,y满足 x+y==10 &&> x,y>=0,每一对x,y表示每一次ping操作发出的10个数据包里有x个成功返回,y个包丢失。现在对服务器alive的定义是,对该服务器发出的所有数据包中有一半成功返回,则代表该服务器live否则dead。
输入格式:第一行一个整数n,表示操作的次数 以下n行表示n次操作,每行三个整数,第一个表示操作的种类,如果为1则表示ping a否则ping> b,第二个表示x,第三个表示y
输出格式:输出两行,分别是服务器a和b的状态,alive输出LIVE,dead输出DEAD
数据保证:2<=n<=10000


这道题就是一个大水题。。统计x个y的和与操作的次数。然后判断输出就ok了

#include <cstdio>
int main(int argc, char const *argv[])
{
	int tot, a, b, aget, bget;
	a = b = aget = bget = 0;
	scanf("%d", &tot);
	for(int i = 0; i < tot; ++i){
		int flag, x ,y;
		scanf("%d%d%d", &flag, &x, &y);
		if(flag == 1){ aget += x; a++;}
		else { bget += x; b++; }
	}
	if(aget*2 >= a*10){ printf("LIVE
");}
	else { printf("DEAD
"); }
	if(bget*2 >= b*10){ printf("LIVE
");}
	else { printf("DEAD
"); }
	return 0;
}

B. Internet Address

题目描述:定义一个网址的标准格式如下:

  • < protocol >://< domain >.ru[/< context >]

protocol是协议类型,只有两种ftp和http
domain是域名,不为空,这里有坑
context是资源文本,可以为空,为空则没有/,否则有/
现给出一个没有标点的网址字符串,要求还原成标准格式
例如

  • 给定输入 httpsunrux
  • 则输出 http://sun.ru/x

输入格式:一行字符串
输出格式:一行字符串,如果多解,输入任意一个解即可
数据保证:输入字符串长度不超过50,一定有解


一样的水题,不过我还是坑了一次。。因为可能输出domain为空的情况

#include <cstdio>

int main(int argc, char const *argv[])
{
	int p = 0;	char s[100]; gets(s);
	if(*s=='h'){ printf("http://");p = 4; 	}
	else { printf("ftp://"); p = 3; }
	do { putchar(s[p++]);} 
	while(!(s[p]=='r'&&s[p+1]=='u'));
	//一开始这里用的while,然后坑了,改成do-while就好了
	printf(".%c%c", s[p], s[p+1]);
	if(s[p+=2]!='') {	printf("/%s", s+p); }
	return 0;
}

C. Game with Coins

题目描述:有n个箱子,每个箱子有a[i]个金币,每次操作可以选定一个数x(2x+1<=n),然后可以从第x个,第2x个,第2*x+1个箱子里取走一枚金币。问对于给定的n和{a1,a2,······,an}最少要多少次能取完所有的箱子里的金币,如果不能取完,输出-1,否则输出最少的次数
输入格式:第一行一个整数n,第二行n个整数,分别是a1,a2,······,an。
输出格式:最少的次数或者-1
数据保证:1<=n<=100,1<=ai<=1000


解题的思路就是:贪心!!
首先判断不可能的情况,也就是输出-1的情况,当n小于3或者n为偶数的时候,肯定无解,因为要求每一次的2x+1<=n,当n是偶数的时候,第n个箱子是无法被取走金币的。。。因为当x=n/2时,2x+1 == n+1 > n了。当x=n/2-1时,2x+1 = n-1 < n,都无法等于n。。
其次是最优策略:每次都取x==n/2直到第n个和第n-1个箱子里的金币空了之后,x减一。这样做的好处是,可以尽快的把最后的箱子清空,并且尽可能多的减少前面的箱子里的金币。
具体实现如下:

#include <cstdio>
#define  max(i,j) ({int _ = (i), __ = (j); _ > __ ? _ : __;})
#define  MAX_LEN 111

int main(int argc, char const *argv[])
{
	int n, ans = 0, a[MAX_LEN]={0};
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i){ 	scanf("%d", a+i); }
	if(n<3 || n%2==0){ printf("-1"); } 
	else {
		for(int i = n/2; i > 0; --i){
			int tmp  = max(a[i*2],a[i*2+1]);
			a[i*2+1] = max(0,a[i*2+1]-tmp);
			a[i*2]   = max(0,a[i*2]-tmp);
			a[i]     = max(0,a[i]-tmp);
			ans += tmp;
		}
		ans += a[1];
		printf("%d", ans);
	}
	return 0;
}

D. Restoring Table

题目描述:Polycarpus 由一个数列a经过一种运算后得到了一个矩阵b,然后他把数列给忘记了。。现在只有矩阵,要求根据矩阵还原数列!
数列到矩阵的转化运算如下:

  • b[i][j] = a[i]&a[j] (i!=j);
  • b[i][j] =-1;(i==j);

输入格式:第一行一个整数n,接下来是一个n*n的矩阵,第i+1行第j列表示矩阵的a[i][j]。
输出格式:一行整数,表示{a1,a2,······,an},多解输出任意解
数据保证:矩阵的第i行第i列一定是-1,一定有解,0<=a[i]<=1e9,1<=n<=100


这道题考察了位运算的知识
具体的思路就是:a[i] = a[i] | b[i][j]; a[j] = a[j] | b[i][j]; 当然i不能等于j,也就是说-1必须跳过。。这道题我似乎是直接AC。。
代码如下:

#include <cstdio>
#define	 MAX_LEN 111

int main(int argc, char const *argv[])
{
	int n, x, ans[MAX_LEN] = {0};
	scanf("%d", &n);
	for(int i = 0; i < n; ++i){
		for(int j = 0; j < n; ++j){
			scanf("%d", &x);
			if(x!=-1){
				ans[i] |= x;
				ans[j] |= x;
			}
		}
	}
	for(int i = 0; i < n; ++i){
		printf("%d ", ans[i]);
	}
	return 0;
}

E. Mishap in Club

题目描述:根据一个club的进出记录,+表示进入一个人,-表示出去一个人,要求算出能判断出的最少人数。呃呃呃这个题目描述不好描述,英文不好,本来是一道水题的。硬生生的被英语坑了。没读懂题目。赛后,在同学的翻译下才弄明白意思。
输入格式:一行字符串,只包含'+'和'-'
输出格式:可能的最少的人数
数据保证:输入字符串长度不超过300


解题思路:记录两个变量,分别表示club外面的人数和里面的人数,有于题意,一开始是未知的,所以初始化为0,然后如果进来了人,判断外面是否有人,有人的话就把外面的人数减一,否则答案ans加1,无论外面是否有人,里面人数加1;如果出去了人,判断里面是否有人,有人则里面人数减一,否则答案ans加1,无论里面是否有人,外面人数加1。
最后输出ans

#include <cstdio>
#define abs(x) ({(x)<0?-(x):(x);})
#define max(i,j) ({int _ = (i), __ = (j); _ > __ ? _ : __;})

int main(int argc, char const *argv[])
{
	char ch; 
	int inside = 0, outside = 0, ans = 0;
	while(scanf("%c", &ch) != EOF && (ch == '-'||ch=='+')){
		if(ch == '+') { 
			inside++;
			if(outside>0) { outside--; }
			else { ans++; }
		}else {
			outside++;
			if(inside>0) { inside--; }
			else { ans++; }
		}
	}
	printf("%d", ans);
	return 0;
}

F. Log Stream Analysis

题目描述:一条warning日志的标准格式是这样的。。

  • "2012-MM-DD HH:MM:SS:MESSAGE" (没有引号).

现在给一个日志文件,要求计算出第一次出现持续n秒的warning数量大于等于m的时间。。
一开始没看懂题目意思。。天坑!!又是英语。。
输入格式:第一行两个整数n和m,然后是若干行日志
输出格式:输出一行时间:2012-MM-DD HH:MM:SS,表示第一次满足要求的时刻(the first moment)。如果无解则输出-1。
数据保证:1<=n,m<=10000,字符串长度不超过5e6。


解题思路:首先可以肯定的是,MESSAGE无用的,每次读入一条warning,把时间全部转化成秒,要考虑月份,年份是确定的2012,维护一个队列,把转化成秒的当前时间加入到队尾,然后判断队首时间加上n是否小于等于当前时间,如果小于等于,则把队首元素出队,循环直到队列为空或者条件不满足。然后判断队列的长度len是否大于等于m,如果大于等于m,则输出当前时间,结束算法!如果warning全部读完了,队列的长度都没有满足大于等于m的条件,则输出-1
实现如下:

#include <cstdio>
#define MAX_LEN 11111111

int  mon[13] = {0,31,60,91,121,152,182,213,244,274,305,335,366};
int  n, m, head, tail;
int  queue[MAX_LEN];
char s[MAX_LEN];

int main(int argc, char const *argv[])
{
	scanf("%d%d
", &n, &m);
	int month, days, hour, minute, second, current;
	while(scanf("2012-%d-%d %d:%d:%d: ", &month, &days, &hour, &minute, &second)!=EOF){
		current = (mon[month-1]+(days-1))*86400+hour*3600+minute*60+second;
		queue[tail++] = current;
		for(;head<tail && queue[head] <= current-n; ++head);
		if(tail-head >= m){
			printf("2012-%02d-%02d %02d:%02d:%02d
", month, days, hour, minute, second);
			return 0;
		}
		gets(s);
	}
	printf("-1
");
	return 0;
}

G. Suggested Friends

题目描述:给一个朋友关系网,朋友关系是相互的,计算每个人的推荐朋友数量(ps:这让我想起了facebook)。用户y能被推荐给用户x需要满足以下条件:

  1. x != y
  2. x和y不是朋友
  3. 用户y拥有最多的和x相同的朋友。

(hint:如果用户z(同样满足条件1和条件2)和用户x有1个共同朋友,而y与x有两个共同朋友,则y应该被推荐给x,而z不会被推荐)
输入格式:第一行一个整数m,表示朋友关系的数量,以下n行每行两个用空格隔开的字符串,表示两个用户的名字,同时这两个用户是朋友关系
输出格式:对于每一个用户,输出一行,每一行由用户名字加该用户的推荐朋友数量,名字和推荐朋友数用空格隔开
数据保证:1<=m<=5000,每个用户在输入中至少出现一次。


解题思路:在图上面用暴力算法。把输入的关系网抽象成一个图,用临接表把图存下来,然后对于每一个用户x,计算他的推荐朋友数量。计算方法是这样的:遍历当前用户x的每一个朋友,打上标记, 然后再遍历每一个除自己外没有标记用户y,计算他的朋友列表里被标记的朋友的数量,这个数量就是当前用户x和用户y的共同好友数量。取最大的共同好友数量的用户个数为当前用户的推荐朋友数量,输出!考虑时间复杂度和空间复杂度,时间复杂度是搜索算法和图算法必须考虑的问题,空间复杂度则是图算法必须考虑的问题。该算法的时间复杂度是O(n^2)。n是用户数量,最大是m的两倍。因此不会超时。空间复杂度,临接表够节省了,一般用了临接表的图不会爆空间的了。
具体实现如下:

#include <cstdio>
#include <cstring>
#define  MAX_NUM 11111

struct adj{
	int point;
	adj *next;
} _adj[MAX_NUM*2], *pbuf=_adj;

int  personnum;
adj* head[MAX_NUM];
char name[MAX_NUM][22];

int  find(char const *s);
void link(int a, int b);

int main(int argc, char const *argv[])
{
	int n;
	scanf("%d", &n);
	for(int i=0; i<n; ++i){
		char s[20];
		scanf("%s", s);
		int x = find(s);
		scanf("%s", s);
		int y = find(s);
		link(x,y);
		link(y,x);
	}
	printf("%d
", personnum);
	for(int i=0; i<personnum; ++i){
		bool vis[MAX_NUM] = {false};
		int  ans = 0, maxfriendnum = 0;
		for(adj* p = head[i]; p!=NULL; p=p->next){
			vis[p->point] = true;
		}
		for(int j = 0; j<personnum; ++j){
			int curfriendnum = 0;
			if(i==j || vis[j]) { continue; }
			for(adj* p = head[j]; p!=NULL; p=p->next){
				if(vis[p->point]){ curfriendnum++; }
			}
			if(curfriendnum==maxfriendnum) { ans++; }
			else if(curfriendnum>maxfriendnum){
				maxfriendnum = curfriendnum;
				ans = 1;
			}
		}
		printf("%s %d
", name[i], ans);
	}
	return 0;
}

int find(char const *s)
{
	int ans = 0;
	for(; ans<personnum && strcmp(name[ans],s)!=0; ++ans);
	if(ans==personnum) { strcpy(name[personnum++],s); }
	return ans;
}

void link(int a, int b)
{
	if(head[a]==NULL) {
		head[a] = pbuf;
	} else {
		pbuf->next    = head[a]->next;
		head[a]->next = pbuf;
	}
	pbuf++->point = b;
}

H. Queries for Number of Palindromes

题目描述:给定一个字符串s和n组询问,询问字符串的子串s(l,r)有多少个回文子串。
输入格式:第一行一个字符串s,第二行一个整数n,以下n行,每行两个整数,表示询问的字串的区间。
输出格式:对于每一个询问,输出一行一个整数,表示字串区间内的回文字串数量。
数据保证:字符串只由小写英文字母构成,长度不超过5000,1<=n<=1e6


解题思路:DP!!!区间DP。。可惜考试的时候我没有把转移方程想出来(神啊,原谅我这个DP渣吧)。后来自己想出来了答案。思路如下图。

DP

状态设计:用f[i][j]来表示区间(i,j)的答案。
转移方程:f[i][j] = f[i+1][j]+f[i][j-1]-f[i+1][j-1]+is[i][j];
is[i][j]表示s(l,r)是否是回文串,如果是is[i][j]1,否则is[i][j]0;
可以看到,f[i][i] == 1;因此只需要构造is[i][j]然后一重循环枚举长度一重循环枚举起点,就可以完美的转移状态了。。长度l从1开始枚举。根据转移方程,等号右边的f都是长度为j-i-1的。。因此,从长度短的转移到长度长的,是不会漏掉状态的。
is[i][j]的构造方法: 首先每一个is[i][i]=1;然后枚举长度l从1到len(s)
is[i][i+l]=(l1||is[i+1][i+l-1])&&s[i]s[i+l];
具体实现如下:

#include <cstdio>
#include <cstring>
#define MAX_LEN 5555

int  ans[MAX_LEN][MAX_LEN];
bool is[MAX_LEN][MAX_LEN];

int main(int argc, char const *argv[])
{
	char s[MAX_LEN];
	scanf("%s", s);
	int len = strlen(s);
	for(int i = 0; i < len; ++i){
		ans[i][i] = is[i][i] = true;
	}
	for(int l = 1; l <= len; ++l){
		for(int i = 0; i < len-l; ++i){
			int j = i+l;
			if((l==1||is[i+1][j-1])&&s[i]==s[j]){
				is[i][j] = true;
			}
		}
	}
	for(int l = 1; l <= len; ++l){
		for(int i = 0; i < len-l; ++i){
			int j = i+l;
			ans[i][j] = ans[i+1][j]+ans[i][j-1]-ans[i+1][j-1]+is[i][j];
		}
	}
	int n, l, r;
	scanf("%d", &n);
	while(n--){
		scanf("%d%d", &l, &r);
		printf("%d
", ans[l-1][r-1]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/SparkRat/p/4535663.html