vjudge个人赛 复习1

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;
}
AC代码 用栈维护

B - 线段的重叠(贪心)

X轴上有N条线段,每条线段包括1个起点和终点。线段的重叠是这样来算的,[10 20]和[12 25]的重叠部分为[12 20]。
给出N条线段的起点和终点,从中选出2条线段,这两条线段的重叠部分是最长的。输出这个最长的距离。如果没有重叠,输出0。
 

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;
}
80分 n^2暴力
/*
    贪心,按照线段左端点升序排序, 
    左端点相等,右端点降序排序 
*/  
#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;  
}
100分 贪心

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,目前的和 
}
60分 暴力
#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;
}
100分 枚举n^3

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;
}
排序+暴力+二分1
#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;
}
排序+暴力+二分2
/*
    还有个很好的思路,先把数组中任意两个数存起来,再从小到大排序,类似二分的思想从两头开始查找,是否满足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;
}
AC代码 直接深搜

F - 数组中和等于K的数对(枚举+二分)

给出一个整数K和一个无序数组A,A的元素为N个互不相同的整数,找出数组A中所有和等于K的数对。例如K = 8,数组A:{-1,6,5,3,4,2,9,0,8},所有和等于8的数对包括(-1,9),(0,8),(2,6),(3,5)。
 

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;
}
AC代码 枚举+二分

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;
}
AC代码 贪心

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);
    }
}
AC代码 唯一分解定理

I - 全排列 (搜索)

给出一个字符串S(可能有重复的字符),按照字典序从小到大,输出S包括的字符组成的所有排列。例如:S = "1312",
输出为:
 
1123
1132
1213
1231
1312
1321
2113
2131
2311
3112
3121
3211

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);
}
75分 暴力
#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));
}
100分 next_permutation

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;
}
100分

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);
    }
}
100分 Tarjan
原文地址:https://www.cnblogs.com/thmyl/p/7527590.html