1. ly的农场
【题目描述】
在农夫ly的农场里,有好多好多的奶牛,但是由于ly喂养得太好了,奶牛们越来越懒了,导致ly不得不采取些措施:要求它们每天早上晨跑,从A农场跑到B农场。从A农场到B农场中有n–2个路口,分别标上号,A农场为1号,B农场为n号,路口分别为2..n–1号,从A农场到B农场有很多条路径可以到达,而ly发现有的路口是必须经过的,即每条路径都经过的路口,ly要把它们记录下来,这样ly就可以先到那个路口,观察奶牛们有没有偷懒,而你的任务就是找出所有必经路口。
【输入文件】
第一行两个数n,m。
接下来m行,每行两个数u和v,表示路口u和v之间有路径直达。
输入数据保证必经路口一定存在,并且每个路口都和A,B农场直接或间接连通。
【输出文件】
第一行一个数m,表示必经路口数目。
接下来m行,按从小到大的顺序依次输出每个必经路口的编号。(不包括起点,终点)
【样例输入】
6 6
1 2
2 4
2 3
3 5
4 5
5 6
【样例输出】
2
2
5
【数据范围】
对于30%的数据,n ≤ 100,m ≤ 1000。
对于100%的数据,n ≤ 2000,m ≤ 8000。
正解:暴力。。。
解题报告:
考场上面打的是tarjan,实际上举得出反例。其实直接暴力枚举就可以了,每次遍历全图,看不经过某点是否可以到达终点。
1 //It is made by jump~ 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 #include <stack> 14 #ifdef WIN32 15 #define OT "%I64d" 16 #else 17 #define OT "%lld" 18 #endif 19 using namespace std; 20 typedef long long LL; 21 const int MAXN = 2011; 22 const int MAXM = 16011; 23 int n,m,ecnt,now; 24 int first[MAXN],next[MAXM],to[MAXM]; 25 bool vis[MAXN]; 26 bool ok; 27 int dui[MAXN],cnt; 28 29 inline int getint() 30 { 31 int w=0,q=0; 32 char c=getchar(); 33 while((c<'0' || c>'9') && c!='-') c=getchar(); 34 if (c=='-') q=1, c=getchar(); 35 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); 36 return q ? -w : w; 37 } 38 39 inline void link(int x,int y){ 40 next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; 41 next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; 42 } 43 44 inline void dfs(int x){ 45 if(x==n) { ok=true; return ;} 46 vis[x]=1; 47 for(int i=first[x];i;i=next[i]){ 48 int v=to[i]; 49 if(!vis[v] && v!=now) dfs(v); 50 if(ok) return ; 51 } 52 } 53 54 inline void solve(){ 55 n=getint(); m=getint(); 56 int x,y; 57 for(int i=1;i<=m;i++) { 58 x=getint(); y=getint(); 59 link(x,y); 60 } 61 62 for(int i=2;i<n;i++) { 63 now=i; ok=false; 64 memset(vis,0,sizeof(vis)); 65 dfs(1); 66 if(!ok) dui[++cnt]=i; 67 } 68 69 printf("%d ",cnt); 70 for(int i=1;i<=cnt;i++) { 71 printf("%d ",dui[i]); 72 } 73 } 74 75 int main() 76 { 77 solve(); 78 return 0; 79 }
2.xqz的难题
【题目描述】
xqz同学最近迷上了位运算,因为他天才般的头脑,题目都过于简单了。不过他今天ms状态不怎么好,被下面这道题打击到了,于是去寻找正在堕落的ld。题目如下:
给定一个数列,设计一个算法支持以下操作:
1. 插入一个数到数列的最后。
2. 在数列中找到一个值为k的数,将它从数列中删去。(数据保证数列中存在值为k的数,若有多个K值,任意删除1外)
3. 求值在L到R间的所有数依次进行xor的值。如果只有一个数满足条件,输出此数。
如果没有数满足,输出0。
(扫盲:xor在电脑的计算器里有,例如:7 xor 6 xor 3 = 2)
数列初始时为空。
【输入文件】
第一行,一个数m,表示操作的个数。
接下来有m行,对于每一行,首先一个数q:
若q=1,则后面一个数k(k ≤106),表示将k插入到数列的最后。
若q=2,则后面一个数k,表示将一个值为k的数删去。
若q=3,则后面两个数L和R,表示输出值在L到R间的所有数依次进行xor的值。
【输出文件】
对于每一个q=3的操作输出答案,每行一个数。
【样例输入】
5
1 5
1 6
3 1 10
2 5
3 1 8
【样例输出】
3
6
【数据范围】
对于30%的数据,m ≤ 2000。
对于100%的数据,m ≤ 100000,答案 ≤ 2^31 - 1。
正解:树状数组
解题报告:
考场上又智障了。我的做法是先离线再离散化,然后树状数组,然而写萎了。实际上直接树状数组不用任何优化就可以了,毕竟只有10的6次方。。。
异或有很多神奇的性质,这道题就体现了这一点。
代码又短又快。
1 //It is made by jump~ 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 #ifdef WIN32 14 #define OT "%I64d" 15 #else 16 #define OT "%lld" 17 #endif 18 using namespace std; 19 typedef long long LL; 20 const int MAXM = 1000011; 21 int a[MAXM]; 22 int n=1000000,m; 23 24 inline int getint() 25 { 26 int w=0,q=0; 27 char c=getchar(); 28 while((c<'0' || c>'9') && c!='-') c=getchar(); 29 if (c=='-') q=1, c=getchar(); 30 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); 31 return q ? -w : w; 32 } 33 34 inline void update(int x){ 35 int val=x; 36 while(x<=n) { 37 a[x]^=val; 38 x+=( x&(-x) ); 39 } 40 } 41 42 inline int query(int x){ 43 int total=0; 44 while(x>0) { 45 total^=a[x]; 46 x-=( x&(-x) ); 47 } 48 return total; 49 } 50 51 inline void solve(){ 52 m=getint(); 53 int x,y; 54 for(int i=1;i<=m;i++) { 55 int ljh=getint(); 56 if(ljh==3) { 57 x=getint();y=getint(); 58 printf("%d ",query(y)^query(x-1)); 59 } 60 else{ 61 x=getint(); 62 update(x); 63 } 64 } 65 } 66 67 int main() 68 { 69 solve(); 70 return 0; 71 }
3.ld的棋盘
【题目描述】
(紧接上一题)“题目beng不难类!”xqz找到了ld,结果遭到了强烈的鄙视…怎么办类?xqz灵光一闪,拿出新买的一个“#”形棋盘,上面有24个格子(如下图)。这些格子上面
有1,2,3三种数字,且每种数字有8格。一开始,这些格子上的数字是随机分布的。ld的任务是移动这些格子使得中间8个格子的数字相同。有8种移动方式,分别标记为A到H,可以理解为拉动4条链,如图的变换为“AC”。问至少需要多少次拉动,才能从初始状态到达目标状态?(保证数据有解)ld顿时木了,于是求助作为OI高手的你。
(图略)
【输入文件】
有多组数据。每组数据包含一行,24个数字,表示从上到下从左到右表示棋盘上的数字。以0结束数据。(友情提示:请注意空间)
【输出文件】
每组数据一行,输出最少移动次数。
【样例输入】
1 1 1 1 3 2 3 2 3 1 3 2 2 3 1 2 2 2 3 1 2 1 3 3
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3
0
【样例输出】
2
4
【数据范围】
对于30%的数据,最大步数 ≤ 7,数据组数 ≤ 10。
对于60%的数据,最大步数 ≤ 12,数据组数 ≤ 20。
对于100%的数据,最大步数 ≤ 12,数据组数 ≤ 30。
正解:搜索
解题报告:
北大信息学夏令营一试第一题,才从北京回来,今天考试又考到了这道题。。。
只不过这道题稍稍弱化了一点。
就直接搜索,迭代加深搜索,确定搜索次数上界,进行搜索。
首先看一下中间的八个格子变成哪个数,1或者2或者3,3次枚举(剪枝:选择需要移动次数最少的)。
确定完变成谁之后就枚举几种移动方式(字母),往下dfs。
这样的话还是会TLE一个点,我们考虑避免一种情况,上一层往上移动,这一层又往下移动,这显然是没必要的,剪掉,就可以AC了。
这题的翻版是POJ2286。
1 //It is made by jump~ 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 #ifdef WIN32 14 #define OT "%I64d" 15 #else 16 #define OT "%lld" 17 #endif 18 using namespace std; 19 typedef long long LL; 20 const int MAXC = 1011; 21 int daan; 22 /* 23 0 1 24 2 3 25 4 5 6 7 8 9 10 26 11 12 27 13 14 15 16 17 18 19 28 20 21 29 22 23 */ 30 int mp[12][12]={{0,2,6,11,15,20,22}/*A*/,{1,3,8,12,17,21,23}/*B*/,{10,9,8,7,6,5,4}/*C*/,{19,18,17,16,15,14,13}/*D*/}; 31 int fan[12] = {5,4,7,6,1,0,3,2}; 32 int zhong[12] = {6,7,8,11,12,15,16,17};//中心的编号 33 int a[45]; 34 35 inline int getint() 36 { 37 int w=0,q=0; 38 char c=getchar(); 39 while((c<'0' || c>'9') && c!='-') c=getchar(); 40 if (c=='-') q=1, c=getchar(); 41 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); 42 return q ? -w : w; 43 } 44 45 inline bool already() { 46 for(int i=0;i<=7;i++) if (a[zhong[i]]!=a[zhong[0]]) return false; 47 return true; 48 } 49 50 inline int coun(int check) { 51 int jishu=0; 52 for(int i=0;i<8;i++) if(a[zhong[i]]!=check) jishu++; 53 return jishu; 54 } 55 56 inline int gu() { 57 int ljh=coun(1); 58 for(int i=2;i<=3;i++) //分别放1、2、3 59 ljh=min(coun(i),ljh); 60 return ljh; 61 } 62 63 inline void move(int x) {//往指定方向拉动一格 64 int cun=a[mp[x][0]]; 65 for(int i=0;i<6;i++) a[mp[x][i]]=a[mp[x][i+1]]; 66 a[mp[x][6]]=cun; 67 } 68 69 inline bool dfs(int d,int maxd,int last) { 70 if(already()) return true; 71 72 if(d+gu()>maxd) return false;//超过上界 73 74 for(int i=0;i<8;i++) { 75 if(last==fan[i]) continue; 76 move(i); 77 if(dfs(d+1,maxd,i)) return true; 78 move(fan[i]);//还原 79 } 80 81 return false; 82 } 83 84 inline void init(){ 85 fan[10]=-1; 86 for(int i=4;i<=7;i++) 87 for(int j=0;j<=6;j++) 88 mp[i][j]=mp[fan[i]][6-j]; 89 } 90 91 inline void solve(){ 92 init(); 93 int xx; 94 while(scanf("%d",&xx)!=EOF && xx) { 95 a[0]=xx; for(int i=1;i<=23;i++) scanf("%d",&a[i]); 96 if(already()) printf("0 "); 97 else { 98 daan=1; 99 for(;;daan++) 100 if(dfs(0,daan,10)) break; 101 } 102 printf("%d ",daan); 103 } 104 } 105 106 int main() 107 { 108 solve(); 109 return 0; 110 }
4.jx的游戏
【题目描述】
jx和ld一样,都在堕落……他现在正沉迷于一个国产数字游戏中。这个游戏看似简单,jx在研究了许多天看了许多次Game over的画面后却发觉,原来在简单的规则下想要赢得这个游戏并不那么容易。不过他发现了这个问题的简化版,即给定一个数列,找出1个连续数列使其和最大。但其本身的问题却很难解决:给定一个数列,找出3个无交集的连续数列使其和最大。显然,你的任务是解决这个原问题,好胜的jx很希望你能帮他解决这个问题赢得这个Game。
【输入文件】
第一行一个数n,表示数列长度。
接下来有n行,每行一个数,第i行为第i个数。
【输出文件】
仅有一个数,表示最大和。
【样例输入】
10
-1
2
3
-4
0
1
-6
-1
1
-2
【样例输出】
7
【数据范围】
数列是非空数列且长度大于等于3
对于30%的数据,n ≤ 200。
对于60%的数据,n ≤ 2000。
对于100%的数据,n ≤ 1000000,答案 ≤ 2^31 - 1。
正解:DP
解题报告:
考场上没什么时间做这道题了。一眼秒题:DP。
短时间内没有设计出状态。考完之后听llg的“满口鬼话”,听懂了这道题的状态。
f[i][j][0、1]表示处理到第i个数,前i-1个数可以分成j块(j<=3),第i个数选或者不选的最大值
丧病出题人卡空间,滚动一下数组就可以了。
转移还是很好想到的,O(1)转移,轻松AC。
1 //It is made by jump~ 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 #ifdef WIN32 14 #define OT "%I64d" 15 #else 16 #define OT "%lld" 17 #endif 18 using namespace std; 19 typedef long long LL; 20 const int MAXN = 1000011; 21 int n,ans; 22 int f[2][4][2]; 23 24 inline int getint() 25 { 26 int w=0,q=0; 27 char c=getchar(); 28 while((c<'0' || c>'9') && c!='-') c=getchar(); 29 if (c=='-') q=1, c=getchar(); 30 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); 31 return q ? -w : w; 32 } 33 34 int main() 35 { 36 n=getint(); 37 38 int jilu=0; 39 for(int i=1;i<=n;i++) { 40 jilu=jilu^1; 41 int x=getint(); 42 f[jilu][1][0]=max(f[!jilu][1][0],f[!jilu][1][1]); 43 f[jilu][1][1]=x+max(0,f[!jilu][1][1]); 44 45 if(i>=2) { 46 f[jilu][2][0]=max(f[!jilu][2][0],f[!jilu][2][1]); 47 f[jilu][2][1]=max(f[!jilu][2][1],max(f[!jilu][1][1],f[!jilu][1][0]))+x; 48 } 49 50 if(i>=3) { 51 f[jilu][3][0]=max(f[!jilu][3][0],f[!jilu][3][1]); 52 f[jilu][3][1]=max(f[!jilu][3][1],max(f[!jilu][2][1],f[!jilu][2][0]))+x; 53 } 54 55 ans=max(f[jilu][3][0],f[jilu][3][1]); 56 } 57 58 printf("%d",ans); 59 return 0; 60 }