福州大学第十三届程序设计竞赛_重现解题报告
来源:互联网 发布:招标文件制作软件 编辑:程序博客网 时间:2024/06/14 02:00
此文章可以使用目录功能哟↑(点击上方[+])
都快要退役了,但是感觉自己还是实力不够,智商捉急...
链接→福州大学第十三届程序设计竞赛_重现
Problem A Calculus Midterm
Accept: 0 Submit: 0
Time Limit: 1000 mSec Memory Limit : 32768 KB
Problem Description
Yellowstar刚考完微积分期中考,微积分期中考卷一共三个部分,8道选择题每题3分,8道填空题,每题2分,10题答题每题6分,总分100分。Yellowstar考完期中考后已经失去了计算能力,他只记得每部分对的题数,现在想请你帮忙计算一下他的得分,能否通过考试。
Input
多组数据,处理到EOF。
每组数据输入3个整数 x,y,z(0<=x<=8,0<=y<=8,0<=z<=10)分别表示选择题、填空题和简答题正确的题数。
Output
每组数据需要输出两行,如果他最少的得分大于等于60 ,第一行输出“I passed the exam.”(双引号不需要输出),第二行输出他的得分;如果他最低得分小于60,第一行输出“Exam was too hard.”(双引号不需要输出),第二行输出他的得分。
Sample Input
0 0 0
Sample Output
100
Exam was too hard.
0
Problem Idea
解题思路:水题,单纯判断3*x+2*y+6*z是否大于等于60
题目链接→Problem 2229 Calculus Midterm
Source Code
/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 100005;const int M = 40;const int inf = 100000000;const int mod = 2009;int main(){ int x,y,z,sum; while(~scanf("%d%d%d",&x,&y,&z)) { sum=x*3+y*2+z*6; if(sum>=60) puts("I passed the exam."); else puts("Exam was too hard.");printf("%d\n",sum); } return 0;}
Problem B 翻翻棋
Accept: 0 Submit: 0
Time Limit: 1000 mSec Memory Limit : 32768 KB
Problem Description
象棋翻翻棋(暗棋)中双方在4*8的格子中交战,有时候最后会只剩下帅和将。根据暗棋的规则,棋子只能上下左右移动,且相同的级别下,主动移动到地方棋子方将吃掉对方的棋子。将和帅为同一级别。然而胜负在只剩下帅和将的时候已定。
Input
第一行T,表示T组数据。
每组数据共有四行字符串,每行字符串共八个字符
’#’表示空格
’*’表示红方帅
’.’表示黑方将
此时红方先走
每组输入之间没有空行。
Output
每组数据输出一行。若为红方赢输出Red win,否则输出 Black win
Sample Input
######.#
#####*##
########
########
Sample Output
Problem Idea
解题思路:水题,判断曼哈顿距离(|x1-x2|+|y1-y2|)的奇偶性,若为奇数,先手必胜;若为偶数,先手必败
题目链接→Problem 2230 翻翻棋
Source Code
/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 100005;const int M = 40;const int inf = 100000000;const int mod = 2009;char s[4][15];int main(){ int t,i,j,x1,y1,x2,y2,d; scanf("%d",&t); while(t--) { for(i=0;i<4;i++) scanf("%s",s[i]); for(i=0;i<4;i++) for(j=0;j<8;j++) if(s[i][j]=='*') x1=i,y1=j;else if(s[i][j]=='.')x2=i,y2=j;d=abs(x2-x1)+abs(y2-y1);if(d%2)puts("Red win");elseputs("Black win"); } return 0;}
Problem C 平行四边形数
Accept: 0 Submit: 0
Time Limit: 2000 mSec Memory Limit : 32768 KB
Problem Description
在一个平面内给定n个点,任意三个点不在同一条直线上,用这些点可以构成多少个平行四边形?一个点可以同时属于多个平行四边形。
Input
多组数据(<=10),处理到EOF。
每组数据第一行一个整数n(4<=n<=500)。接下来n行每行两个整数xi,yi(0<=xi,yi<=1e9),表示每个点的坐标。
Output
每组数据输出一个整数,表示用这些点能构成多少个平行四边形。
Sample Input
0 1
1 0
1 1
2 0
Sample Output
Problem Idea
解题思路:给你n个点,求能构成多少个平行四边形。首先,根据平行四边形的一个定义:对角线平分的四边形为平行四边形
故而,我们可以求出任意两点的中点,由于题目说任意三个点都不在同一直线上,所以排除了以下情况:
那么,接下来要做的就是计算k对有共同中点的点集(每个点集包含两个点)能组成多少个平行四边形,即k(k-1)/2
题目链接→Problem 2231 平行四边形数
Source Code
/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 505;const int M = 40;const int inf = 100000000;const int mod = 2009;struct node{int x,y;}s[N],d[N*N];bool cmp(node x,node y){if(x.x!=y.x)return x.x<y.x;return x.y<y.y;}int main(){ int n,i,j,p,c; while(~scanf("%d",&n)) { p=c=0; for(i=0;i<n;i++) scanf("%d%d",&s[i].x,&s[i].y); for(i=0;i<n;i++) for(j=i+1;j<n;j++) { d[p].x=s[i].x+s[j].x; d[p].y=s[i].y+s[j].y; p++; } sort(d,d+p,cmp); for(i=0,j=1;i<p&&j<p;i=j,j++) { while(d[j].x==d[i].x&&d[j].y==d[i].y) j++; c+=(j-i-1)*(j-i)/2;}printf("%d\n",c); } return 0;}
Problem D 炉石传说
Accept: 0 Submit: 0
Time Limit: 1000 mSec Memory Limit : 32768 KB
Problem Description
GG学长虽然并不打炉石传说,但是由于题面需要他便学会了打炉石传说。但是传统的炉石传说对于刚入门的GG学长来说有点复杂,所以他决定自己开发一个简化版的炉石传说。
在简化版的炉石传说中:
每个随从只有生命值和攻击力,并且在你的回合下,你的每只随从在本回合下只能选择一个敌方随从进行攻击。当两个随从a,b交战时,a的生命值将减去b的攻击力,b的生命值将减去a的攻击力,(两个伤害没有先后顺序,同时结算)。如果a或b的生命值不大于0,该随从将死亡。
某一次对局中,GG学长和对手场面上均有n个随从,并且是GG学长的回合。由于GG学长是个固执的boy,他一定要在本回合杀死对方所有随从,并且保证自己的随从全部存活。他想知道能否做到。
Input
第一行为T,表示有T组数据。T<=100。
每组数据第一行为n,表示随从数量(1 <= n <= 100)
接下来一行2 * n个数字a1, b1, a2, b2, ... , an, bn (1 <= ai, bi <= 100)
表示GG学长的n个随从,ai表示随从生命,bi表示随从攻击力
接下来一行2 * n个数字c1, d1, c2, d2, ... , cn, dn (1 <= ci, di <= 100)
表示对手的n个随从,ci表示随从生命,di表示随从攻击力。
Output
每组数据,根据GG是否能完成他的目标,输出一行”Yes”或”No”。
Sample Input
3
4 4 5 5 6 6
1 1 2 2 3 3
3
4 4 5 5 6 6
1 4 2 4 3 4
Sample Output
No
Problem Idea
解题思路:因为GG学长和对手均有n个随从,GG学长先手,且每个随从只能攻击对方的一个随从,很明显是二分匹配问题,而解决二分匹配的问题,常常可以转化为网络流来做,那简单了,只要我们把图给建出来,剩下的就交给网络流模板了
首先,我们要添加一个源点1,和汇点2*n+2,源点1到点2~n+1都有一条流量为1的路,而点n+2~2*n+1到汇点2*n+2也都有一条流量为1的路
其次考虑点2~n+1与点n+2~2*n+1之间的情况,很明显,只有在GG学长的一个随从X攻击大于等于对方随从Y的血量,且GG学长的该随从X血量大于对方随从Y的攻击,这种情况才有一条从点X到点Y的流量为1的路
最后,我们只要判断该图的最大流是否为n即可
题目链接→Problem 2232 炉石传说
Source Code
/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000") #include<stdio.h> #include<string.h> #include<stdlib.h> #include<queue> #include<stack> #include<math.h> #include<vector> #include<map> #include<set> #include<cmath> #include<string> #include<algorithm> #include<iostream> #define exp 1e-10 using namespace std; const int N = 505; const int M = 1005; const int inf = 1000000007; const int mod = 2009; #include<iostream> #include<cstdio> #include<cstring> using namespace std; int Map[N][N];//存图 int pre[N];//记录当前点的前驱 int level[N];//记录距离标号 int gap[N];//gap常数优化 int NV,NE; //入口参数vs源点,vt汇点 int SAP(int vs,int vt,int n) { memset(pre,-1,sizeof(pre)); memset(level,0,sizeof(level)); memset(gap,0,sizeof(gap)); gap[0]=n; int v,u=pre[vs]=vs,maxflow=0,aug=inf; while(level[vs]<n) { //寻找可行弧 for(v=1;v<=n;v++) if(Map[u][v]>0&&level[u]==level[v]+1) break; if(v<=n) { pre[v]=u; u=v; if(v==vt) { aug=inf; //寻找当前找到的一条路径上的最大流 for(int i=v;i!=vs;i=pre[i]) if(aug>Map[pre[i]][i]) aug=Map[pre[i]][i]; maxflow+=aug; //更新残留网络 for(int i=v;i!=vs;i=pre[i]) { Map[pre[i]][i]-=aug; Map[i][pre[i]]+=aug; } u=vs;//从源点开始继续搜 } } else { //找不到可行弧 int minlevel=n; //寻找与当前点相连接的点中最小的距离标号 for(v=1;v<=n;v++) if(Map[u][v]>0&&minlevel>level[v]) minlevel=level[v]; gap[level[u]]--;//(更新gap数组)当前标号的数目减1; if(gap[level[u]]==0) break;//出现断层 level[u]=minlevel+1; gap[level[u]]++; u=pre[u]; } } return maxflow; } int a[N],b[N],c[N],d[N]; int main() { int n,m,u,v,cap; int t,i,j; scanf("%d",&t); while(t--) { memset(Map,0,sizeof(Map)); scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]); for(i=1;i<=n;i++) scanf("%d%d",&c[i],&d[i]); for(i=1;i<=n;i++) { Map[1][i+1]=1;for(j=1;j<=n;j++) {if(a[i]>d[j]&&b[i]>=c[j]) Map[i+1][j+n+1]=1;Map[j+n+1][2*n+2]=1; } } if(SAP(1,n*2+2,n*2+2)==n) puts("Yes");elseputs("No"); } return 0; }
Problem E ~APTX4869
Accept: 0 Submit: 0
Time Limit: 1000 mSec Memory Limit : 32768 KB
Problem Description
为了帮助柯南回到一米七四,阿笠博士夜以继日地研究APTX4869的解药。他得出了如下结果:
1.解药由n种原料构成;
2.对于两种不同的的原料a,b,它们之间有个影响值f(a,b);
3.需要把原料分成两个部分X,Y,每部分中至少有一种原料;
4.解药的效果由分别属于X,Y的原料之间,最小的影响值决定,即
博士需要你帮忙求出:在所有的方案中,最大的效果值可以是多少?
Input
多组数据(<=10),处理到EOF。
每组数据输入第一行为一个正整数n。
接下去是一个n行n列的整数矩阵,同一行的数以空格隔开。矩阵第i行j列表示第i种和第j种材料的影响值f(i,j)。给出的矩阵是对称的,即f(i,j)=f(j,i)。当i=j时,f(i,i)没有意义,矩阵该处的值为-1。
2<=n<=800。当i!=j时,0<=f(i,j)<=1000000;当i=j时,f(i,j)=-1。
Output
每组数据输出一行,表示最大可能的效果值。
Sample Input
-1 100 300
100 -1 200
300 200 -1
Sample Output
Problem Idea
解题思路:为了效果值能够尽可能大,我们应该避免选到小的f(i,j)
那换种思路,只要不存在相对较小的f(i,j),那我们就可以避免取到小的f(i,j)
那如何使相对较小的f(i,j)不存在呢?很简单,只要第i种材料与第j种材料属于同一类,那么第i种材料与第j种材料之间就不存在影响值
而我们需要做的就是,首先二分答案,然后将所有小于二分值的影响值f(i,j)中的两种材料划分到同一类,最后判断有几个连通块就行
判连通块的方法有很多种,本人采用并查集来实现
题目链接→Problem 2233 ~APTX4869
Source Code
/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 805;const int M = 40;const int inf = 100000000;const int mod = 2009;int n,s[N][N],c[N],b[N*N/2],p;bool v[N];int fun(int x) { if(c[x]!=x) c[x]=fun(c[x]); return c[x]; } void init(){for(int i=1;i<=n;i++)c[i]=i;memset(v,false,sizeof(v));}bool judge(int x){int i,j,k,t=0;init();for(i=1;i<=n;i++)for(j=i+1;j<=n;j++)if(s[i][j]<x)c[fun(i)]=fun(j); for(i=1;i<=n;i++){k=fun(i);if(!v[k]){t++;v[k]=true;}}if(t>1)return true;return false;}void Binary()//二分答案 { int l=0,r=p-1,ans=0; while(l<r) { int mid=(l+r)>>1; if(judge(b[mid])){l=mid+1;ans=max(ans,b[mid]);} else r=mid; } if(l==r&&judge(b[l])) ans=max(ans,b[l]); printf("%d\n",ans);}int main(){ int i,j; while(~scanf("%d",&n)) { p=0; for(i=1;i<=n;i++) for(j=1;j<=n;j++) { scanf("%d",&s[i][j]); if(j>i) b[p++]=s[i][j]; } sort(b,b+p); p=unique(b,b+p)-b;Binary(); } return 0;}/*3-1 100 300100 -1 200300 200 -14-1 200 200 100200 -1 200 100200 200 -1 200100 100 200 -14-1 200 100 100200 -1 200 100100 200 -1 200100 100 200 -1*/
Problem F 牧场物语
Accept: 0 Submit: 0
Time Limit: 1000 mSec Memory Limit : 32768 KB
Problem Description
小茗同学正在玩牧场物语。该游戏的地图可看成一个边长为n的正方形。
小茗同学突然心血来潮要去砍树,然而,斧头在小茗的右下方。
小茗是个讲究效率的人,所以他会以最短路程走到右下角,然后再返回到左上角。并且在路上都会捡到/踩到一些物品,比如说花朵,钱和大便等。
物品只能被取最多一次。位于某个格子时,如果格子上还有物品,就一定要取走。起点和终点上也可能有物品。
每种物品我们将为其定义一个价值,当然往返之后我们取得的物品的价值和越大越好。但是小茗同学正在认真地玩游戏,请你计算出最大的价值和。
Input
多组数据(<=10),处理到EOF。
第一行输入正整数N(N≤100),表示正方形的大小。
接下来共N行,每行N个整数Ai,j(|Ai,j|≤10^9),表示相应对应位置上物品的价值。值为0表示没有物品。
Output
每组数据输出一个整数,表示最大价值和。
Sample Input
11 14
16 12
Sample Output
Problem Idea
解题思路:小茗从左上角走到右下角再走回左上角,这个问题可以转换为两个人同时从左上角到右下角,使得路上取得最大权值
显然,此题就变成了一道DP题
令ans[k][(x1,y1)][(x2,y2)]表示1号小茗第k步走到点(x1,y1),同时2号小茗第k步走到(x2,y2),两人取得的权值之和
很容易的,我们可以得到转移方程
ans[k][(x1,y1)][(x2,y2)]=max{
ans[k-1][(x1-1,y1)][(x2-1,y2)],
ans[k-1][(x1,y1-1)][(x2,y2-1)],
ans[k-1][(x1-1,y1)][(x2,y2-1)],
ans[k-1][(x1,y1-1)][(x2-1,y2)]
}
另外,题目说小茗会以最短路程走到右下角,那么,说明小茗从起点到终点需要k=n+n-2步
而k与坐标点(x,y)的关系为k+2=x+y,因为0步的时候小茗位于(0,0)
那么我们就可以将转移方程简化掉两维,用步数和横坐标来替换纵坐标,如此,转移方程为
ans[k][x1][x2]=max{
ans[k-1][x1-1][x2-1],
ans[k-1][x1][x2],
ans[k-1][x1-1][x2],
ans[k-1][x1][x2-1]
}
步数增加之后,即便横坐标不变,纵坐标必定是增加了的此题另一个优化点是步数k
因为k状态只和k-1状态有关,我们可以利用滚动数组来进一步优化空间
有个注意的地方是s[i][j]的取值范围(<=10^9),故而初始化的时候要把-inf弄得小一些,1e12足够了,因为这个我还错了几次,供大家借鉴
题目链接→Problem 2234 牧场物语
Source Code
/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 105;const int M = 40;const __int64 inf = 1e12;const int mod = 2009;int s[N][N];__int64 ans[2][N][N];int main(){ int n,i,j,k,c; while(~scanf("%d",&n)) { for(i=0;i<=n;i++) for(j=0;j<=n;j++) { ans[0][i][j]=ans[1][i][j]=-inf; if(i!=0&&j!=0)scanf("%d",&s[i][j]); } ans[0][1][1]=s[1][1]; for(c=0,k=1;k<2*n-1;k++) { c^=1; for(i=1;i<=min(k+1,n);i++) for(j=1;j<=min(k+1,n);j++) {ans[c][i][j]=max(max(ans[c^1][i][j],ans[c^1][i-1][j-1]),max(ans[c^1][i-1][j],ans[c^1][i][j-1])); if(i==j)ans[c][i][j]+=s[i][k+2-i];elseans[c][i][j]+=s[i][k+2-i]+s[j][k+2-j]; } } printf("%I64d\n",ans[c][n][n]); } return 0;}
Problem G 国王的出游
Accept: 0 Submit: 0
Time Limit: 1000 mSec Memory Limit : 32768 KB
Problem Description
黑暗之王有一片棋盘般的疆土,这片疆土有2*10^9行,有2*10^9列。假设这块疆土的行从上到下编号1到2*10^9,它的列从左到右也编号1到2*10^9。我们可以把第i行第j列的方格记为坐标(i,j)。
但是这偌大棋盘只有被给的N个线状区域才是允许通行的。每个线状区域被三个参数描述,ri,ai,bi(ai<= bi)。ri代表给定线状区域的行编号,ai代表线状区域的起始列编号,bi代表末尾编号。
现在国王想要从一个给定坐标方格(x0,y0)通过最小移动步数到达终点坐标方格(x1,y1),而且只能通过上述给出的允许通行的线状区域。
国王移动一步只能发生在相邻的方格之间。此外,如果两个方格至少共享一个点我们便认为他们相邻。
Input
有多组数据(<=30),处理到文件尾(EOF)。
每组数据第一行包含四个整数,x0, y0, x1, y1(1 <= x0, y0, x1, y1 <= 2*10^9),分别代表国王的初始坐标和终点坐标。
第二行有一个整数N (1 <= N <= 10^5),代表有N条可通行的线状区域。
接下里会有N行,第i行包含三个整数,ri,ai, bi (1 <= ri, ai bi <= 2*10^9),含义看题面。
数据允许线状区域会有交叉或者嵌套。
数据保证国王的起点方格和终点方格都是可通行的,并且两个区域不相同。另外保证所有线状区域的的长度总和不超过10^5。
1 <= x0, y0, x1, y1, ri, ai, bi<= 2*10^9,1 <= N <= 10^5
Output
如果没有一条道路使得国王从起始区域到达终点区域,则输出 -1。
否则,则输出国王的从起始区域到达终点区域的最小步数。
Sample Input
3
5 3 8
6 7 11
5 2 5
1 1 2 10
2
1 1 3
2 6 10
Sample Output
-1
Problem Idea
解题思路:一开始相信绝大多数人都会被这棋盘的规模吓一跳,2*10^9 * 2*10^9,然后束手无策,但是只要仔细读题,就会发现其中的入手点
题目保证所有线状区域的的长度总和不超过10^5,原来之前的都是虚张声势
那么,题目就容易下手了,相信绝大多数人这个时候应该都能想到bfs(广搜)
但是难点在于如何保存已经走过的格子,因为坐标规模比较大,之前的visit[][]数组必然是已经开不出来
那我们可以转换一下思路,我最先想到的是用STL里的map来实现
TLE代码(下面有正确代码,往下看)如下:
/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 100005;const int M = 40;const int inf = 100000000;const int mod = 2009;struct node{int x,y,c;node(){}node(int x0,int y0,int c0):x(x0),y(y0),c(c0){}};int g[8][2]={{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1}};map<pair<int,int>,bool> m;int bfs(int x0,int y0,int x1,int y1){int i;node u;queue<node> q;q.push(node(x0,y0,0));m[make_pair(x0,y0)]=false;while(!q.empty()){u=q.front();q.pop();for(i=0;i<8;i++){x0=u.x+g[i][0];y0=u.y+g[i][1];if(x0==x1&&y0==y1) return u.c+1;if(m[make_pair(x0,y0)]){m[make_pair(x0,y0)]=false;q.push(node(x0,y0,u.c+1));}}}return -1;}int main(){int x0,y0,x1,y1,n,i,r,a,b;while(~scanf("%d%d%d%d",&x0,&y0,&x1,&y1)){m.clear();scanf("%d",&n);while(n--){scanf("%d%d%d",&r,&a,&b);for(i=a;i<=b;i++)m[make_pair(r,i)]=true;}if(x0==x1&&y0==y1) puts("0"); else printf("%d\n",bfs(x0,y0,x1,y1));} return 0;}
题目链接→Problem 2235 国王的出游
当然这个想法解决单组已经足够了(详情见CodeForces 242C),但是对于T(<=30)还有点力不从心,原因在于每次的map搜索还是比较耗时的,那我们再进行进一步的优化,考虑将能走的格子坐标保存下来,每次二分来找判断该点是否能走,这样便成功得以解决,正确代码如下:
Source Code
/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 100005;const int M = 40;const int inf = 100000000;const int mod = 2009;struct node{int x,y,c;node(){}node(int x0,int y0,int c0):x(x0),y(y0),c(c0){}};int p,g[8][2]={{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1}};pair<int,int> m[N];bool v[N];int bfs(int x0,int y0,int x1,int y1){int i,k;node u;queue<node> q;q.push(node(x0,y0,0));k=lower_bound(m,m+p,make_pair(x0,y0))-m;if(m[k]==make_pair(x0,y0))v[k]=false;while(!q.empty()){u=q.front();q.pop();for(i=0;i<8;i++){x0=u.x+g[i][0];y0=u.y+g[i][1];if(x0==x1&&y0==y1) return u.c+1; k=lower_bound(m,m+p,make_pair(x0,y0))-m;if(m[k]==make_pair(x0,y0)&&v[k]){v[k]=false;q.push(node(x0,y0,u.c+1));}}}return -1;}int main(){int x0,y0,x1,y1,n,i,r,a,b;while(~scanf("%d%d%d%d",&x0,&y0,&x1,&y1)){p=0;memset(v,false,sizeof(v));scanf("%d",&n);while(n--){scanf("%d%d%d",&r,&a,&b);for(i=a;i<=b;i++){m[p]=make_pair(r,i);v[p++]=true;}}sort(m,m+p);p=unique(m,m+p)-m;if(x0==x1&&y0==y1) puts("0"); else printf("%d\n",bfs(x0,y0,x1,y1));} return 0;}
Problem H 第十四个目标
Accept: 0 Submit: 0
Time Limit: 1000 mSec Memory Limit : 32768 KB
Problem Description
目暮警官、妃英里、阿笠博士等人接连遭到不明身份之人的暗算,柯南追踪伤害阿笠博士的凶手,根据几起案件现场留下的线索发现凶手按照扑克牌的顺序行凶。在经过一系列的推理后,柯南发现受害者的名字均包含扑克牌的数值,且扑克牌的大小是严格递增的,此外遇害者与毛利小五郎有关。
为了避免下一个遇害者的出现,柯南将可能遭到暗算的人中的数字按关联程度排列了出来,即顺序不可改变。柯南需要知道共有多少种可能结果,满足受害人名字出现的数字严格递增,但是他柯南要找出关键的证据所在,所以这个任务就交给你了。
(如果你看不懂上面在说什么,这题是求一个数列中严格递增子序列的个数。比如数列(1,3,2)的严格递增子序列有(1)、(3)、(2)、(1,3)、(1,2),共5个。长得一样的但是位置不同的算不同的子序列,比如数列(3,3)的答案是2。)
Input
多组数据(<=10),处理到EOF。
第一行输入正整数N(N≤100 000),表示共有N个人。
第二行共有N个整数Ai(1≤Ai≤10^9),表示第i个人名字中的数字。
Output
每组数据输出一个整数,表示所有可能的结果。由于结果可能较大,对1 000 000 007取模后输出。
Sample Input
1 3 2
Sample Output
Problem Idea
解题思路:找数列中严格递增子序列的个数
记a[i]表示结尾为i的序列个数,考虑到Ai会达到10^9,故而先离散化
然后,我们要计算a[i],必须知道所有的a[j](j<i)之和,这时,可以考虑用树状数组
因为树状数组c[]表示的正是前多少项之和,详情见 链接→树状数组学习笔记
当然也可以自己写线段树
题目链接→Problem 2236 第十四个目标
Source Code
/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 100005;const int M = 40;const int inf = 100000000;const int mod = 1000000007;int s[N],a[N],n;__int64 c[N];int lowbit(int t) {//计算c[t]展开的项数 return t&(-t); } void update(int i,__int64 x) { while(i<=n) { c[i]=(c[i]+x)%mod; i+=lowbit(i); } } __int64 Sum(int n) //求前n项的和. { __int64 sum=0; while(n>0) { sum=(sum+c[n])%mod; n-=lowbit(n); } return sum; } int main(){ int i,j,k; __int64 ans; while(~scanf("%d",&n)) { memset(c,0,sizeof(c)); ans=0; for(i=0;i<n;i++) { scanf("%d",&s[i]); a[i]=s[i]; } sort(a,a+n); k=unique(a,a+n)-a; for(i=0;i<n;i++) s[i]=lower_bound(a,a+k,s[i])-a+1; /*for(i=0;i<n;i++)printf("%d ",s[i]);*/for(i=0;i<n;i++)update(s[i],Sum(s[i]-1)+1);/*for(i=1;i<=n;i++)printf("%I64d ",c[i]);*/ printf("%I64d\n",Sum(n)); } return 0;}
菜鸟成长记
- 福州大学第十三届程序设计竞赛_重现解题报告
- 福州大学第十三届程序设计竞赛_重现
- 福州大学第十三届程序设计竞赛_重现 F 牧场物语
- 福州大学第十三届程序设计竞赛_重现总结
- 【福州大学第十三届程序设计竞赛_重现】D - 炉石传说(二分图最大匹配)
- foj Problem 2232 炉石传说 福州大学第十三届程序设计竞赛_重现 二分图
- 福州大学第十二届程序设计竞赛 解题报告
- 福州大学第十四届程序设计竞赛_重现赛 I
- 福州大学第十四届程序设计竞赛_重现赛 J
- 福州大学第十四届程序设计竞赛_重现赛
- 周赛题 (福州大学第十三届程序设计竞赛)
- 福州大学第十四届程序设计竞赛_重现赛 A Salty Fish(思路)
- (补题心路)福州大学第十四届程序设计竞赛_重现赛
- 福州大学第十四届程序设计竞赛_重现赛 A Salty Fish
- 福州大学第十四届程序设计竞赛-重现赛(Record)
- 福州大学第十三届程序设计竞赛 Problem B 翻翻棋【bfs】
- FZU-2231 平行四边形数 From 福州大学第十三届程序设计竞赛
- 福州大学第十三届程序设计竞赛题解【8/9】
- 从头认识多线程-2.10 同步代码块的特殊现象:一半同步,一半异步
- HDOJ(HDU) 2113 Secret Number(遍历数字位数的每个数字)
- 加减乘除计算器
- java中Static&Final的使用方法,&&&Java中两个主要的抽象Abstract&&Implement
- linux下安装php报错configure: error: Cannot find MySQL header files under /usr/include/mysql.
- 福州大学第十三届程序设计竞赛_重现解题报告
- C++this指针&&友元
- JAVA——TCP传输
- HDU5572(计算几何+精度)
- oracle学习之路——关于常量的输出
- Helloworld -SilverN
- Hello world S.B.S.
- vb6.0 Access数据库
- 常用函数(1)