A - 大鱼吃小鱼(栈)
有N条鱼每条鱼的位置及大小均不同,他们沿着X轴游动,有的向左,有的向右。游动的速度是一样的,两条鱼相遇大鱼会吃掉小鱼。从左到右给出每条鱼的大小和游动的方向(0表示向左,1表示向右)。问足够长的时间之后,能剩下多少条鱼?Input第1行:1个数N,表示鱼的数量(1 <= N <= 100000)。
第2 - N + 1行:每行两个数A[i], B[i],中间用空格分隔,分别表示鱼的大小及游动的方向(1 <= A[i] <= 10^9,B[i] = 0 或 1,0表示向左,1表示向右)。Output输出1个数,表示最终剩下的鱼的数量。Sample Input
5 4 0 3 1 2 0 1 0 5 0
Sample Output
2
/* 对于第i条鱼,如果方向朝右进栈。向左的话,跟栈每一个元素j比较,如果i>j 那么j死,继续访问j前面的元素(知道栈空或者死),,, 否则i死, 继续遍历i+1以及后面的元素 */ #include<iostream> #include<cstdio> using namespace std; #define maxn 100010 int st[maxn],top,n,cnt; int main(){ scanf("%d",&n);cnt=n; int x,y; for(int i=1;i<=n;i++){ scanf("%d%d",&x,&y); if(y==1)st[++top]=x; else{ while(top){ if(x>st[top]){top--;cnt--;} else {cnt--;break;} } } } cout<<cnt; }
B - 线段的重叠(贪心)
Input第1行:线段的数量N(2 <= N <= 50000)。
第2 - N + 1行:每行2个数,线段的起点和终点。(0 <= s , e <= 10^9)Output输出最长重复区间的长度。Sample Input
5 1 5 2 4 2 8 3 7 7 9
Sample Output
4
#include<iostream> #include<cstdio> #include<algorithm> #define maxn 50010 using namespace std; int n; struct node{ int l,r; bool operator < (const node a)const{ return l<a.l; } }e[maxn]; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&e[i].l,&e[i].r); sort(e+1,e+n+1); int ans=0; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++){ if(e[i].r>e[j].r) ans=max(ans,e[j].r-e[j].l); else ans=max(ans,e[i].r-e[j].l); } cout<<ans; }
/* 贪心,按照线段左端点升序排序, 左端点相等,右端点降序排序 */ #include<iostream> #include<algorithm> #include<cmath> using namespace std; const int MAX=50005; struct Node{ int b,e; }; Node a[MAX]; int cmp(Node p1,Node p2){ if(p1.b<p2.b) return 1; else if(p1.b==p2.b&&p1.e>p2.e) return 1; return 0; } int main() { int n; while(cin>>n){ for(int i=0;i<n;i++){ cin>>a[i].b>>a[i].e; } sort(a,a+n,cmp); int res=0; Node s=a[0]; for(int i=1;i<n;i++){ if(a[i].e<=s.e){//线段i在线段i-1内 res=max(res,a[i].e-a[i].b); } else if(a[i].b<=s.e&&a[i].e>s.e){ res=max(res,s.e-a[i].b); s=a[i];//选择最靠后的线段 } else s=a[i]; } cout<<res<<endl; } return 0; }
C - 3个数和为0(暴力枚举)
给出一个长度为N的无序数组,数组中的元素为整数,有正有负包括0,并互不相等。从中找出所有和 = 0的3个数的组合。如果没有这样的组合,输出No Solution。如果有多个,按照3个数中最小的数从小到大排序,如果最小的数相等则按照第二小的数排序。
Input第1行,1个数N,N为数组的长度(0 <= N <= 1000)
第2 - N + 1行:A[i](-10^9 <= A[i] <= 10^9)Output如果没有符合条件的组合,输出No Solution。
如果有多个,按照3个数中最小的数从小到大排序,如果最小的数相等则继续按照第二小的数排序。每行3个数,中间用空格分隔,并且这3个数按照从小到大的顺序排列。Sample Input
7 -3 -2 -1 0 1 2 3
Sample Output
-3 0 3 -3 1 2 -2 -1 3 -2 0 2 -1 0 1
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; #define maxn 1010 int n,a[maxn],b[4]; void dfs(int pos,int cnt,int sum){ if(cnt==4){ if(sum==0){ for(int i=1;i<=3;i++)printf("%d ",b[i]); printf(" "); } return; } if(pos>n)return; for(int i=pos;i<=n;i++){ b[cnt]=a[i]; dfs(i+1,cnt+1,sum+a[i]); } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); sort(a+1,a+n+1); dfs(1,1,0);//到底几个数了,已经确定了几个数-1,目前的和 }
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; #define maxn 1010 int n,a[maxn]; bool flag; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); sort(a+1,a+n+1); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) for(int k=j+1;k<=n;k++) if(a[i]+a[j]+a[k]==0){ flag=1; printf("%d %d %d ",a[i],a[j],a[k]); } if(!flag)printf("No Solution"); return 0; }
D - 复杂度n^2log(n^2)(折半枚举)
给出N个整数,你来判断一下是否能够选出4个数,他们的和为0,可以则输出"Yes",否则输出"No"。Input第1行,1个数N,N为数组的长度(4 <= N <= 1000)
第2 - N + 1行:A[i](-10^9 <= A[i] <= 10^9)Output如果可以选出4个数,使得他们的和为0,则输出"Yes",否则输出"No"。Sample Input
5 -1 1 -5 2 4
Sample Output
Yes
#include<iostream> #include<cstdio> #include<map> #include<algorithm> using namespace std; int n,a[1010]; map<int,bool>f; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); sort(a+1,a+n+1); int mid=n/2; for(int i=1;i<=mid;i++){ for(int j=i+1;j<=mid;j++){ int x=a[i]+a[j]; f[x]=1; } } for(int i=mid+1;i<=n;i++){ for(int j=i+1;j<=n;j++){ int x=-(a[i]+a[j]); if(f[x]){ printf("Yes"); return 0; } } } printf("No"); return 0; }
#include<iostream> #include<stdio.h> #include<algorithm> using namespace std; int a[1100]; int main(){ int n; scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&a[i]); sort(a,a+n); for(int i=0;i<n;i++) for(int j=i+1;j<n;j++){ int k,m,sum; k=j+1; m=n-1; while(k<m) { sum=a[i]+a[j]+a[k]+a[m]; if(sum<0)k++; else if(sum>0)m--; else{ k++;m--; printf("Yes "); return 0; } } } printf("No "); return 0; }
/* 还有个很好的思路,先把数组中任意两个数存起来,再从小到大排序,类似二分的思想从两头开始查找,是否满足sum1+sum2==0 并且两个坐标不重叠,每个点只选一次 */ #include <iostream> #include <stdio.h> #include <algorithm> using namespace std; int a[1100]; struct node{ int x, y; int sum; }P[1100*1100]; int cmp(node a, node b) { return a.sum < b.sum; } int main(){ int n; scanf("%d", &n); for(int i=0;i<n;i++)scanf("%d",&a[i]); int k=0; for(int i=0;i<n;i++) for(int j=i+1;j<n;j++) { P[k].x=a[i]; P[k].y=a[j]; P[k++].sum=a[i]+a[j]; } sort(P,P+k,cmp); int l=0,r=k-1; int flag=0; while(l<r){ if (P[l].sum+P[r].sum==0&&P[l].x!=P[r].x&&P[l].y!=P[r].y&&P[l].x!=P[r].y&&P[l].y!=P[r].x){ flag = 1; break; } else if(P[l].sum+P[r].sum<0)l++; else r--; } if (flag)printf("Yes "); else printf("No "); return 0; }
E - 和为K的组合(搜索)
给出N个正整数组成的数组A,求能否从中选出若干个,使他们的和为K。如果可以,输出:"Yes",否则输出"No"。Input第1行:2个数N, K, N为数组的长度, K为需要判断的和(2 <= N <= 20,1 <= K <= 10^9)
第2 - N + 1行:每行1个数,对应数组的元素A[i] (1 <= A[i] <= 10^6)Output如果可以,输出:"Yes",否则输出"No"。Sample Input
5 13 2 4 6 8 10
Sample Output
No
#include<iostream> #include<cstdio> using namespace std; int n,k,a[30]; bool flag; void dfs(int pos,int sum){ if(sum==k){ flag=1; return; } if(pos>=n)return; if(flag)return; for(int i=pos+1;i<=n;i++) dfs(i,sum+a[i]); } int main(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;i++)scanf("%d",&a[i]); if(k==0){ printf("Yes"); return 0; } dfs(0,0); if(flag){ printf("Yes"); return 0; } else printf("No"); return 0; }
F - 数组中和等于K的数对(枚举+二分)
Input第1行:用空格隔开的2个数,K N,N为A数组的长度。(2 <= N <= 50000,-10^9 <= K <= 10^9)
第2 - N + 1行:A数组的N个元素。(-10^9 <= A[i] <= 10^9)Output第1 - M行:每行2个数,要求较小的数在前面,并且这M个数对按照较小的数升序排列。
如果不存在任何一组解则输出:No Solution。Sample Input
8 9 -1 6 5 3 4 2 9 0 8
Sample Output
-1 9 0 8 2 6 3 5
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int k,n,a[50010]; bool flag; int main(){ //freopen("Cola.txt","r",stdin); scanf("%d%d",&k,&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); sort(a+1,a+1+n); for(int i=1;i<n;i++){ int l=i+1,r=n; int x=k-a[i]; int pos=lower_bound(a+i+1,a+n+1,x)-a; if(a[pos]!=x)continue; else printf("%d %d ",a[i],a[pos]),flag=1; } if(!flag){ printf("No Solution"); return 0; } return 0; }
G - 活动安排问题(贪心&线段覆盖)
有若干个活动,第i个开始时间和结束时间是[Si,fi),同一个教室安排的活动之间不能交叠,求要安排所有活动,最少需要几个教室?
Input第一行一个正整数n (n <= 10000)代表活动的个数。
第二行到第(n + 1)行包含n个开始时间和结束时间。
开始时间严格小于结束时间,并且时间都是非负整数,小于1000000000Output一行包含一个整数表示最少教室的个数。Sample Input
3 1 2 3 4 2 9
Sample Output
2
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 10010 using namespace std; int n; bool vis[maxn]; struct node{ int s,t; bool operator < (const node b)const{ return t>b.t; } }a[maxn]; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d%d",&a[i].s,&a[i].t); sort(a+1,a+n+1); memset(vis,0,sizeof(vis)); int ans=0; while(1){ int p,st; bool flag=0; for(int i=1;i<=n;i++){ if(!vis[i]){ p=i; vis[p]=1; flag=1; break; } } if(!flag)break; ans++;st=p; for(int i=st+1;i<=n;i++){ if(!vis[i]&&a[i].t<=a[p].s){ vis[i]=1; p=i; } } } printf("%d",ans); return 0; }
H - Anigram单词(唯一分解定理)
一个单词a如果通过交换单词中字母的顺序可以得到另外的单词b,那么定义b是a的Anigram,例如单词army和mary互为Anigram。现在给定一个字典,输入Q个单词,从给出的字典中找出这些单词的Anigram。
Input第1行:1个数N,表示字典中单词的数量。(1 <= N <= 10000)
第2 - N + 1行,字典中的单词,单词长度 <= 10。
第N + 2行:查询的数量Q。(1 <= Q <= 10000)
第N + 3 - N + Q - 2行:用作查询的单词,单词长度 <= 10。Output共Q行,输出Anigram的数量,相同的2个单词不算Anigram,如果没有输出0。Sample Input
5 add dad bad cad did 3 add cac dda
Sample Output
1 0 2
/* 首先定义a-z分别为从1开始的素数,例如a=2,b=3,c=5,d=7这样. 然后定义一个函数int Func(char* data) 然后函数里面对于每个单词的每个字母进行相乘,例如单词abc就等于5*2*3=30;返回30 然后再到字典里面去找与这个单词位数相同且Func返回值相同的单词就好了. 这个算法主要用到了一个数学界的定理,如果一个正整数是n个素数相乘的结果,那么这个正整数有且只有一组素数解. 定理证明过程 z = a1*a2*....*an. 用反证法,如果有解,那么 z = a1*a2*.....*an-2*b*c,(b c代表如果有其它解的情况)两式相除得到下面的 对于这个题来说,还要将答案减去字典中与该字符串相等的字符串个数 */ #include<iostream> #include<cstdio> #include<cstring> #include<map> using namespace std; int su[100],cnt,n; map<string,int>q1; map<long long,int>q2; bool vis[200]; int main(){ scanf("%d",&n); for(int i=2;i<=100;i++){ if(!vis[i])su[++cnt]=i; for(int j=1;j<=cnt&&su[j]*i<=100;j++){ vis[su[j]*i]=1; if(i%su[j]==0)break; } } long long ans=1; string s; for(int i=1;i<=n;i++){ cin>>s; q1[s]++; int len=s.length(); long long num=1; for(int j=0;j<len;j++){ int x=s[j]-'a'+1; num*=x; } q2[num]++; } scanf("%d",&n); for(int i=1;i<=n;i++){ cin>>s; int ans=0; int len=s.length(); long long num=1; for(int j=0;j<len;j++){ int x=s[j]-'a'+1; num*=x; } ans=q2[num]-q1[s]; printf("%d ",ans); } }
I - 全排列 (搜索)
Input输入一个字符串S(S的长度 <= 9,且只包括0 - 9的阿拉伯数字)Output输出S所包含的字符组成的所有排列Sample Input
1312
Sample Output
1123 1132 1213 1231 1312 1321 2113 2131 2311 3112 3121 3211
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #define maxn 10 using namespace std; int q[maxn],n,a[maxn]; char s[maxn]; bool vis[maxn]; map<string,int>v; void dfs(int pos){ if(pos==n+1){ string c=""; for(int i=1;i<=n;i++)c+=a[i]+'0'; if(v[c])return; v[c]=1; for(int i=1;i<=n;i++)printf("%d",a[i]); puts(""); return; } for(int i=1;i<=n;i++){ if(!vis[i]){ vis[i]=1; a[pos]=q[i]; dfs(pos+1); vis[i]=0; } } } int main(){ scanf("%s",s+1); n=strlen(s+1); for(int i=1;i<=n;i++)q[i]=s[i]-'0'; sort(q+1,q+n+1); dfs(1); }
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int a[11],n; char s[11]; int main(){ scanf("%s",s); n=strlen(s); for(int i=0;i<n;i++)a[i]=s[i]-'0'; sort(a,a+n); do{ for(int i=0;i<n;i++)cout<<a[i]; cout<<endl; }while(next_permutation(a,a+n)); }
J - 旋转字符串 (STL)
S[0...n-1]是一个长度为n的字符串,定义旋转函数Left(S)=S[1…n-1]+S[0].比如S=”abcd”,Left(S)=”bcda”.一个串是对串当且仅当这个串长度为偶数,前半段和后半段一样。比如”abcabc”是对串,”aabbcc”则不是。
现在问题是给定一个字符串,判断他是否可以由一个对串旋转任意次得到。
Input第1行:给出一个字符串(字符串非空串,只包含小写字母,长度不超过1000000)Output对于每个测试用例,输出结果占一行,如果能,输出YES,否则输出NO。Sample Input
aa ab
Sample Output
YES NO
/* 对串无论旋转多少次都是对串,所以直接判断当前字符串是否是对串即可 */ #include<iostream> #include<cstring> #include<cstdio> using namespace std; string s; int main(){ while(cin>>s){ int l=s.length(); if(l%2!=0){ puts("NO"); continue; } l/=2; string s1=s.substr(0,l); string s2=s.substr(l,l); if(s1==s2)puts("YES"); else puts("NO"); } return 0; }
K - 畅通工程 (Tarjan)
Input测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。
Output对每个测试用例,在1行里输出最少还需要建设的道路数目。
Sample Input
4 2 1 3 4 3 3 3 1 2 1 3 2 3 5 2 1 2 3 5 999 0 0
Sample Output
1 0 2 998
/* 答案=强联通分量个数-1 */ #include<iostream> #include<cstdio> #include<cstring> #define maxn 1010 #define min(a,b) (a)<(b)?(a):(b) using namespace std; int n,m,num,head[maxn]; struct node{ int to,pre; }e[maxn*maxn]; void Insert(int from,int to){ e[++num].to=to; e[num].pre=head[from]; head[from]=num; } int dfn[maxn],low[maxn],st[maxn],top,cnt; bool in[maxn]; bool Tarjan(int u){ cnt++;dfn[u]=low[u]=cnt;st[++top]=u;in[u]=1; for(int i=head[u];i;i=e[i].pre){ int v=e[i].to; if(!dfn[v]){ Tarjan(v); low[u]=min(low[u],low[v]); } else if(in[v])low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]){ while(st[top]!=u){ in[st[top]]=0; top--; } in[u]=0; top--; } } int main(){ while(1){ memset(in,0,sizeof(in)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(head,0,sizeof(head)); memset(e,0,sizeof(e)); num=0;cnt=0; scanf("%d",&n); if(n==0)return 0; scanf("%d",&m); int x,y; for(int i=1;i<=m;i++){ scanf("%d%d",&x,&y); Insert(x,y);Insert(y,x); } int sum=0; for(int i=1;i<=n;i++){ if(!dfn[i]){ sum++; Tarjan(i); } } printf("%d ",sum-1); } }