• 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;
    }
    
  • 相关阅读:
    分布式计算框架——MapReduce
    Hadoop总结
    HBase原理总结
    LeetCode牛客148题
    LeetCode刷题[Python版]
    【Linux】netstat命令详解
    基于docker搭建Prometheus+Grafana监控(一)
    Zabbix笔记一:zabbix安装
    【Linux】vi/vim中如果替换
    【redis】Redis学习:redis测试注意点
  • 原文地址:https://www.cnblogs.com/SparkRat/p/4535663.html
Copyright © 2020-2023  润新知