PAT 甲级 题目简要记录(3)

1101 Quick Sort

题目大意:给出一个含N个不同数的序列,该序列是某趟快速排序的中间结果,求哪些数可能是该趟的基准数据pivot

解题思路

题目等价于求哪些数满足:左边区间的数均小于它,右边区间的数均大于它

这题关键在于跑测试数据容易超时

题目再等价于求哪些数满足:大于它左边区间的最大值(maxArr[i]),小于它右边区间的最小值(minArr[i])

这样就可以从暴力代码的复杂度O(n^2)降到O(n)

// 编程关键的代码段
int minArr[MAXN];  // [i, N]区间中的最小数值
int maxArr[MAXN];  // [1, i]区间中的最大数值
maxArr[0]=0;
for(int i=1; i<=N; i++) {
    cin>>input[i];
    maxArr[i] = maxArr[i-1] > input[i] ? maxArr[i-1] : input[i];
}
minArr[N+1]=INT_MAX;
for(int i=N; i>0; i--) {
    minArr[i] = minArr[i+1] < input[i] ? minArr[i+1] : input[i];
    if(minArr[i]>=input[i] && maxArr[i]<=input[i])
        ans.push_back(input[i]);
}

1102 Invert a Binary Tree

题目大意:构造一棵二叉树,并输出层序序列和中序序列。

题目较为简单

1103 Integer Factorization

题目大意:给定N、K、P三个数,求K个数,使得这K个数的P次方的和 恰好等于N

解题思路:我是用了DFS求解

#include <iostream>
#include <cmath>
using namespace std;
const int MAXN=401;
int N, K, P;
int temp[MAXN],resPath[MAXN],maxKsum=0;
int flag=0;  // 该变量记录是否找到一个解
int exp(int n,int p) { // 求n的p次幂  快速幂算法
    int res=1;
    while(p>0) {
        if(p%2==1)
            res*=n;
        p/=2;
        n*=n;
    }
    return res;
}
int GetMax() {  // 判断k个数中,可能的最大值,控制递归边界
    for(int i=1; i<=400; i++) {
        if(exp(i,P)>=(N-K+1))
            return i;
    }
    return -1;
}
bool jud() {
    for(int i=K; i>=1; i--) {
        if(temp[i]>resPath[i])
            return true;
    }
    return false;
}
void Copy() {
    for(int i=1; i<=K; i++)
        resPath[i] = temp[i];
}
void DFS(int startnum,int curSum,int stackLevel,int ksum) {
    if(stackLevel==K) {
        if(curSum==N) {
            flag=1;
            temp[stackLevel]=startnum;
            if(ksum>maxKsum) {
                maxKsum = ksum;
                Copy();
            } else if(ksum==maxKsum && jud()) {
                Copy();
            }
        }
    } else {
        if(curSum>=N)  // 剪枝
            return;
        temp[stackLevel]=startnum;
        for(int i=startnum; i>=1; i--)
            DFS(i,curSum+exp(i,P),stackLevel+1,ksum+i);
    }
}
int main() {
    cin>>N>>K>>P;
    DFS(GetMax(),0,0,0);
    if(flag==0)
        cout<<"Impossible"<<endl;
    else {
        cout<<N<<" = ";
        for(int i=1; i<=K; i++) {
            if(i>1)
                cout<<" + ";
            cout<<resPath[i]<<"^"<<P;
        }
        cout<<endl;
    }
    return 0;
}

1104 Sum of Number Segments(重要)

题目大意:n个数,有n(1+n)/2种排列组合,组合里所有元素总和代表该组合的值,求所有排列组合的值的总和

解题思路:题意转换为求每个数 Ni 在所有组合里出现的次数 Ti\sum_{i=1}^{N} Ni*Ti就是所求结果。

其实一看到这个题目,跟数学的相关性很强,就可以考虑是否存在一个函数F,使得F(N, i) = Ti

T1T2T3T4T5T6T7
N=11
N=222
N=3343
N=44664
N=558985
N=66101212106
N=7712151615127

不难确定函数F:F(N, i) = i * (N-i+1) = Ti,所以\sum_{i=1}^{N}Ni*(i*(N-i+1))就是题目需要的结果

        但是这个题我做的时候,真的没有考虑到它对double精度的要求很高。因为题目给出的Ni都是很小的小数,多次的double相加会造成一定的误差!!!,导致代码未通过测试用例。解决的办法是:将输入值乘以1000,sum值用long long型计算,最后输出除以1000.0保留两位小数

浮点数精度举例

为什么是乘以1000

        我测试了几次,发现乘以100或者10000均不能通过测试用例,自己猜测:题目要求的精度是小数点后2位,所以乘以10的( 2+1 )次幂

#include <cstdio>
int main() {
    double elem;
    long long sum=0;
    int N;
    scanf("%d",&N);
    for(int i=1; i<=N; i++) {
        scanf("%lf",&elem);
        sum+=(long long)(elem*1000)*i*(N-i+1);
    }
    printf("%.2lf\n",sum/1000.0);
    return 0;
}

1106 Lowest Price in Supply Chain

题目大意:寻找一棵树所处层级最小的叶结点,确定最小层级,并确定这样的叶结点有多少个

解题思路:采用DFS+剪枝的方法

#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;
const int MAXN=1e5+10;
vector<int> graph[MAXN];
int minLevel=-1,num=-1;
double getPrice(double r,double p,int level) {
    double res = p;
    for(int i=1; i<=level; i++) {
        res*=(1.0+r);
    }
    return res;
}
void DFS(int root,int level) {
    int len = graph[root].size();
    if(len==0) { // 到达叶结点
        if(minLevel==-1 || level<minLevel) {
            minLevel=level,num=1;
        } else if(level==minLevel) {
            num++;
        }
    } else if(minLevel==-1 || level<minLevel) { // 剪枝,若level>=minLevel,且当前结点不是叶结点,则该路径永远不会到达最优值
        for(int i=0; i<len; i++)
            DFS(graph[root][i],level+1);
    }
}
int main() {
    int N,K,elem;
    double P,r;
    cin>>N>>P>>r;
    for(int i=0; i<N; i++) {
        cin>>K;
        for(int j=1; j<=K; j++) {
            cin>>elem;
            graph[i].push_back(elem);
        }
    }
    DFS(0,0);
    printf("%.4lf %d\n",getPrice(r/100.0,P,minLevel),num);
    return 0;
}

1107 Social Clusters

题目大意:一个兴趣部落里,成员有共同的兴趣爱好(即一个成员至少能在这个部落找到另一个成员,他们有一个共同的爱好;不是要求所有人都必须有一个共同爱好),求出每个兴趣部落有多少人,并降序输出对应的人数。

解题思路:用并查集的算法解题。起初,每个兴趣爱好即代表一个部落;若一个人有两个及以上的爱好,就相当于把这些爱好原先对应的部落进行合并,成为一个新的较大的部落;当输入完毕,部落的划分也就完毕,进行排序操作后输出对应的结果即可。

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN=1001;
int arr[MAXN];  // 拥有第i个hobby的人数是arr[i]
int father[MAXN];  // 记录哪几个hobby可以组成一个聚落
int sum[MAXN];  // 选择一个根结点代表一个聚落,记录每个聚落有多少人
bool cmp(int x,int y) {
    return x > y;
}
void Init() {
    for(int i=0; i<MAXN; i++) {
        father[i] = i;
        sum[i]=0;
        arr[i]=0;
    }
}
int Find(int x) {
    if(x!=father[x])
        father[x] = Find(father[x]);   // 进行了路径压缩
    return father[x];
}
void Union(int x,int y) {
    int fax = Find(x);
    int fay = Find(y);
    if(fax!=fay) {
        father[fax] = fay;
    }
}
int main() {
    Init();
    int N,K,elem,elem2;
    cin>>N;
    for(int i=1; i<=N; i++) {
        scanf("%d: %d",&K,&elem);
        arr[elem]++;
        for(int i=2; i<=K; i++) {
            cin>>elem2;
            // arr[elem2]++;  这句话必须注释掉,因为聚落合并后,人数会有重叠的地方
            Union(elem,elem2);
        }
    }
    for(int i=1; i<MAXN; i++)
        sum[Find(i)]+=arr[i];
    sort(sum,sum+MAXN,cmp);
    int pos=0;
    while(sum[pos]>0)
        pos++;
    cout<<pos<<endl;
    for(int i=0; i<pos; i++) {
        if(i>0)
            cout<<" ";
        cout<<sum[i];
    }
    cout<<endl;
    return 0;
}

1108 Finding Average(重要)

题目大意:一道纯粹考字符串操作的题目

解题思路

遇到考字符串的题目真的要多尝试用sscanf来处理,如果用熟练了真的方便好多

这里参考大神的代码,我本来自己手动写了判断字符串的函数,代码提交测试的时候,一直通不过测试,只好放弃。实在是判断的细节太多了,题目有些又没讲清楚。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int main() {
    char old[1000],now[1000];
    int N,num=0;
    double sum=0;
    cin>>N;
    for(int i=0; i<N; i++) {
        double temp;
        scanf("%s",old);
        sscanf(old,"%lf",&temp);
        sprintf(now,"%.2lf",temp);
        int flag=1;
        for(int j=0; j<strlen(old) && flag==1; j++)
            if(old[j]!=now[j])
                flag=0;
        if(flag==0 || temp<-1000 || temp>1000){
            printf("ERROR: %s is not a legal number\n",old);
            continue;
        }
        else {
            num++;
            sum+=temp;
        }
    }
    if(num==1)
        printf("The average of 1 number is %.2lf\n",sum);
    else if(num>1)
        printf("The average of %d numbers is %.2lf\n",num,sum/num);
    else
        printf("The average of 0 numbers is Undefined\n");
    return 0;
}

但是有个奇怪的错误是,在本地编译器环境跑的时候,把字符数组的大小改为100,会报奇怪的错误,过段时间再测试,又能跑通了。难道是跟电脑本地动态环境有关系吗?

1109 Group Photo

就是一道排序+模拟题

#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN=1e4+10;
struct Person {
    int height;
    string name;
    Person() {}
    Person(int h,string n):height(h),name(n) {}
    bool operator< (const Person& y) const {
        if(height!=y.height)
            return height < y.height;
        return name > y.name;
    }
} person[MAXN];
void PrintRow(int left,int right) { // [left, right]是闭区间
    int len = right-left+1,flag=0;
    int pos = len%2==0 ? left : left+1;
    for(; pos<right; pos+=2) {
        if(flag==0)
            flag=1;
        else
            cout<<" ";
        cout<<person[pos].name;
    }
    for(pos=right; pos>=left; pos-=2) {
        if(flag==0)
            flag=1;
        else
            cout<<" ";
        cout<<person[pos].name;
    }
    cout<<endl;
}
int main() {
    int N,K,h;
    string elem;
    cin>>N>>K;
    for(int i=1; i<=N; i++) {
        cin>>elem>>h;
        person[i]  =Person(h,elem);
    }
    sort(person+1,person+1+N);
    int rowLen=N/K;
    PrintRow(1+rowLen*(K-1),N);
    for(int row=K-1; row>=1; row--)
        PrintRow(1+rowLen*(row-1),rowLen*row);
    return 0;
}

1110 Complete Binary Tree

题目大意:给定一棵二叉树,结点个数为N,判断其是否是完全二叉树

解题思路:对树进行层序遍历,用-1表示子结点为空,若遍历到的前N个结点都是不为空的结点,则该树为完全二叉树;反之则不是。

1111 Online Map

就是一道考Dijkstra算法的知识,解题思路可以参考这篇文章

1112 Stucked Keyboard

        又是一道字符串的操作题,感觉直接看代码吧。很搞笑的是,题目给的样例数据k=3,我写代码的时候直接用了3而不是k,半天没反应过来这个低级错误

#include <iostream>
#include <map>
#include <climits>
#include <vector>
using namespace std;
const int MAXN = 26+10+1+1;
int isStucked[MAXN],vis[MAXN];
map<char,int> myMap;  // 给字符编号,简化后续处理
map<int, char> myMap2;
void Init() {
    for(int i=0; i<MAXN; i++)
        isStucked[i]=1,vis[i]=0;
    int index=0;
    for(int i=0; i<26; i++) {
        char temp = 'a' + i;
        myMap[temp] = index;
        myMap2[index++]=temp;
    }
    for(int i=0; i<10; i++) {
        char temp = '0' + i;
        myMap[temp] = index;
        myMap2[index++]=temp;
    }
    myMap['_'] = index;
    myMap2[index++]='_';
}
int main() {
    Init();
    int k,j;
    string str,res="";
    vector<char> resKey;
    cin>>k>>str;
    int len = str.size();   // 由于题目保证会有一个stucked key,k>1,所以len>1一定成立
    for(int i=0; i<len;) { // 这里没有考虑进最后一个字符
        for(j=1; j<k&&i+j<len; j++)
            if(str[i]!=str[i+j])
                break;
        if(j<k)
            isStucked[myMap[str[i]]]=0,i++;
        else
            i+=k;
    }
    for(int i=0; i<len;) {
        res+=str[i];
        if(isStucked[myMap[str[i]]]==1) {
            if(vis[myMap[str[i]]]==0) {
                resKey.push_back(str[i]);
                vis[myMap[str[i]]]=1;
            }
            i+=k;
        } else {
            i++;
        }
    }
    for(int i=0; i<resKey.size(); i++)
        cout<<resKey[i];
    cout<<endl<<res<<endl;
    return 0;
}

1113 Integer Set Partition

就是一道很简单的排序题

1114 Family Property

并查集的一道题目,不知道为什么我做了好久,可能我的方法用得不对

#include <iostream>
#include <vector>
#include <set>
#include <climits>
#include <algorithm>
#include <cstdio>
using namespace std;
const int MAXN=10001;
struct Person {
    int M;
    int area;
    Person():M(-1),area(-1) {}
} persons[MAXN];
struct Family {
    int ID;
    set<int> members;
    int numArea;
    int sumArea;
    Family():ID(INT_MAX),numArea(-1),sumArea(-1) {}
    bool operator< (const Family& y) const {
        int num1 = members.size();
        int num2 = y.members.size();
        if(numArea==-1)
            return false;
        if(y.numArea==-1)
            return true;
        if(sumArea*num2 != y.sumArea*num1)
            return sumArea*num2 > y.sumArea*num1;
        return ID < y.ID;
    }
} family[MAXN];
int father[MAXN];
void Init() {
    for(int i=0; i<MAXN; i++) {
        father[i]=i;
    }
}
int Find(int x) {
    if(x!=father[x])
        father[x] = Find(father[x]);  // 实现了路径压缩
    return father[x];
}
void Union(int x,int y) {
    int fax = Find(x);
    int fay = Find(y);
    if(fax!=fay) {
        father[fax] = fay;
    }
}
int main() {
    Init();
    int N;
    cin>>N;
    for(int i=0; i<N; i++) {
        int id,fa,ma,k,elem;
        cin>>id>>fa>>ma>>k;
        if(fa!=-1)
            Union(id,fa);
        if(ma!=-1)
            Union(id,ma);
        for(int j=0; j<k; j++) {
            cin>>elem;
            Union(id,elem);
        }
        cin>>persons[id].M>>persons[id].area;
    }
    for(int i=0; i<MAXN; i++) {
        int fa = Find(i);
        family[fa].members.insert(i);
        family[fa].ID = min(family[fa].ID, i);
        if(persons[i].M!=-1) {
            if(family[fa].numArea==-1)
                family[fa].numArea=persons[i].M;
            else
                family[fa].numArea+=persons[i].M;
        }
        if(persons[i].area!=-1) {
            if(family[fa].sumArea==-1)
                family[fa].sumArea=persons[i].area;
            else
                family[fa].sumArea+=persons[i].area;
        }
    }
    sort(family,family+MAXN);
    int numFamily = 0;
    while(numFamily<N && family[numFamily].sumArea!=-1)
        numFamily++;
    cout<<numFamily<<endl;
    for(int i=0; i<numFamily; i++) {
        int mem = family[i].members.size();
        printf("%04d %d %.3lf %.3lf\n",family[i].ID,mem,(double)family[i].numArea/(double)mem,(double)family[i].sumArea/(double)mem);
    }
    return 0;
}

1115 Counting Nodes in a BST

一道简单的二叉搜索树

1116 Come on! Let's C

一道简单的分支判断题

1117 Eddington Number

这题其实就是排序题,我理解完题意,竟然用树状数组做,结果还超时了,真的是小题大做。

做题的时候切忌,要多想想几种可能的算法,然后尽量选择编程容易、效率高的那个,有时候不要给自己增加代码难度。

// 这段代码已通过测试用例
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN=1e5+10;
int arr[MAXN];
int main() {
    int N,i,j,res=-1;
    cin>>N;
    for(i=0; i<N; i++)
        cin>>arr[i];
    sort(arr,arr+N);
    for(int i=N;i>=0;i--){
        if(i==0){  // 这个是针对测试点4:所有arr[i]均为 0
            cout<<i<<endl;
            break;
        }
        if(arr[N-i]>i){   // 有i-1个数大于等于arr[N-i], 这句判断等价于至少有i个数大于i
            cout<<i<<endl;
            break;
        }
    }
    return 0;
}
// 强行弄的树状数组算法,并未通过测试用例
#include <iostream>
using namespace std;
const int MAXN=1e5+10;
int lowbit(int x) {
    return x&(-x);
}
int arr[MAXN];
int GetSum(int x) {
    int res=0;
    for(int i=x; i>0; i-=lowbit(i))
        res+=arr[i];
    return res;
}
void Update(int x,int val) {
    for(int i=x; i<MAXN; i+=lowbit(i))
        arr[i]+=val;
}
int main() {
    fill(arr,arr+MAXN,0);
    int N,elem,maxNum=0;
    cin>>N;
    for(int i=0; i<N; i++) {
        cin>>elem;
        maxNum=max(maxNum,elem);
        Update(elem,1);
    }
    int flag=0;
    for(int i=min(maxNum,N); i>0; i--) {
        if(N-GetSum(i)>=i) {
            flag=1;
            cout<<i<<endl;
            break;
        }
    }
    if(flag==0)
        cout<<0<<endl;
    return 0;
}

1118 Birds in Forest

题目大意:求有多少只鸟和多少棵树,并判断两只鸟是否在同一棵树上

解题思路:一道基础的并查集

1119 Pre- and Post-order Traversals(重要)

题目大意:给出一棵二叉树的先序、后序遍历序列(树的点权值均不相同),求它的中序遍历序列(若有多种可能,输出一种结果即可)

解题思路:见代码中标注的两处注释,它们是解题的关键。有关二叉树的知识点可以参考这篇文章

#include <iostream>
using namespace std;
const int MAXN=31;
struct TreeNode {
    int data;
    TreeNode* left;
    TreeNode* right;
    TreeNode():left(NULL),right(NULL) {}
    TreeNode(int d):data(d),left(NULL),right(NULL) {}
};
int preOrder[MAXN],postOrder[MAXN],flag=0,flag2=0;
TreeNode* Create(int prel,int prer,int postl,int postr) {
    if(prel>prer)
        return NULL;
    else {
        TreeNode* root = new TreeNode(preOrder[prel]);
        if(prer-prel==1) {  // 当子树所有结点总数之和只有1时,这个惟一的结点可能是左子树,也可能是右子树,因而答案不唯一
            flag=1;
            root->left = Create(prer,prer,postl,postl);
        } else if(prer-prel>1) {
            int sepPos = prel+1;  // sepPos就是右子树的根结点,这个很关键
            for(; sepPos<=prer; sepPos++) {
                if(preOrder[sepPos]==postOrder[postr-1])
                    break;
            }
            root->left = Create(prel+1,sepPos-1,postl,(postr-1)-(prer-sepPos+1));
            root->right = Create(sepPos,prer,(postr-1)-(prer-sepPos+1)+1,postr-1);
        }
        return root;
    }
}
void InOrderPrint(TreeNode* root) {
    if(root!=NULL) {
        InOrderPrint(root->left);
        if(flag2==0)
            flag2=1;
        else
            cout<<" ";
        cout<<root->data;
        InOrderPrint(root->right);
    }
}
int main() {
    int N;
    cin>>N;
    for(int i=0; i<N; i++)
        cin>>preOrder[i];
    for(int i=0; i<N; i++)
        cin>>postOrder[i];
    TreeNode* root = Create(0,N-1,0,N-1);
    if(flag==0)
        cout<<"Yes"<<endl;
    else
        cout<<"No"<<endl;
    InOrderPrint(root);
    cout<<endl;
    return 0;
}

1120 Friend Numbers

简单的模拟题,用到了set类型辅助解题

#include <iostream>
#include <set>
using namespace std;
int GetFriendID(int x) {
    int res=0;
    while(x>0) {
        res+=(x%10);
        x/=10;
    }
    return res;
}
int main() {
    int N,elem;
    cin>>N;
    set<int> mySet;
    for(int i=0; i<N; i++) {
        cin>>elem;
        mySet.insert(GetFriendID(elem));
    }
    cout<<mySet.size()<<endl;
    for(set<int>::iterator it=mySet.begin(); it!=mySet.end(); it++) {
        if(it!=mySet.begin())
            cout<<" ";
        cout<<(*it);
    }
    return 0;
}

1121 Damn Single

        一道简单的模拟题,用两个数组分别记录一个人是否在聚会中、一个人的另一个伴侣(伴侣:婚姻的另一半;若无伴侣则用-1表示)。题目中需要注意的情况是:

  • 一对夫妻可能只有一个人参加了聚会,另一个人没来,那么这个参加了的人就是lonely的
  • 输出格式中,编号必须是5位的格式,不足5位用0表示(测试点3)
  • 若参加聚会的人中没有lonely的,在输出中不要多输出一个换行符(测试点1)

1122 Hamiltonian Cycle

        给定一个无向图和若干路径,判断这些路径是否是汉密尔顿回路。这题不是很复杂。如何判断是否是汉密尔顿回路?

  • 若图有N个结点,则回路的结点个数只能是N+1
  • 路径首尾是相同的结点
  • 回路有N个不同的结点(均是图对应的N个结点)
  • 回路相邻结点必须在无向图中是联通的

1123 Is It a Complete AVL Tree

  • 这道题就是考AVL树的基础操作,相关代码可以参考这篇博文,注意当树发生变化(如左旋、右旋、插入新结点)时,千万不要忘记调用updateHeight函数,否则结果会出错(细节有时候就容易忘记)
  • 如何判断是否是完全二叉树:层次遍历中,若队列前N个元素中存在空节点,说明不是完全二叉树;反之则是。(代码可以参考下面)
void LevelOrder(TreeNode* root) {
    queue<TreeNode*> myQueue;
    int flag=0,num=0,flag2=1;
    myQueue.push(root);
    while(!myQueue.empty()) {
        TreeNode* elem = myQueue.front();
        myQueue.pop();
        num++;
        if(elem==NULL) {
            if(num<=N) {
                flag2=0;
            }
            continue;
        }
        if(flag==0)
            flag=1;
        else
            cout<<" ";
        cout<<elem->data;
        myQueue.push(elem->left);
        myQueue.push(elem->right);
    }
    if(flag2==1)
        cout<<endl<<"YES"<<endl;
    else
        cout<<endl<<"NO"<<endl;
}

1124 Raffle for Weibo Followers

  • 一道比较简单的模拟题,用一个变量标记下一个获奖者对应的indice
  • 用set类型变量保存已获奖的人员,避免重复获奖
  • 用queue保存获奖人员,以便按输入顺序输出他们的名字

1125 Chain the Ropes

题目大意:给N段绳子,要求把它们合并为一串,合并规则是:每次合并两个绳子,新绳子长度是原来两段绳子长度之和的一半。求最后得到的一串绳子,长度的最大值。

解题思路

  • 由题意知,越早参与合并的绳子,它为最终绳子长度的贡献权重就越低
  • 所以应用贪心策略,让越长的绳子越迟参与合并、获得越大的贡献权重
  • 类似哈夫曼树的求解过程
  • 注意题目的精度问题,由于是向下取整操作,把每个绳子的长度取它的10倍保存,最后输出再除以10取整即可
#include <iostream>
#include <queue>
using namespace std;
int main() {
    priority_queue<double,vector<double>,greater<double> > myQueue;
    int N,elem;
    cin>>N;
    for(int i=0; i<N; i++) {
        cin>>elem;
        myQueue.push(elem*10);
    }
    while(myQueue.size()>1) {
        double x = myQueue.top();
        myQueue.pop();
        double y = myQueue.top();
        myQueue.pop();
        myQueue.push((x+y)/2);
    }
    cout<<(int)myQueue.top()/10<<endl;
    return 0;
}

1126 Eulerian Path

题目大意:给定一个无向图,求每个结点的度的大小,并判断该图的类型(有三个类型候选:Eulerian, semi-Eulerian, or non-Eulerian)

解题思路

这题编程实现不难,关键在于第一段话的理论。很容易漏掉的信息是,“connected graphs”,编程的时候千万不要忘记连通图这个前提需要判断,可以用并查集算法实现连通图的判断。

  • Eulerian:连通图+所有结点度数为偶数
  • semi-Eulerian:连通图+有且仅有2个结点度数为奇数
  • non-Eulerian:其他所有情况
#include <iostream>
using namespace std;
// 不要忽略连通图的条件限定,这个用并查集可以判断
const int MAXN=501;
int graph[MAXN][MAXN]= {0},father[MAXN];
void Init(int n) {
    for(int i=0; i<=n; i++)
        father[i] = i;
}
int Find(int x) {
    if(x!=father[x])
        father[x] = Find(father[x]);  // 路径压缩
    return father[x];
}
void Union(int x,int y) {
    int fax = Find(x);
    int fay = Find(y);
    if(fax!=fay)
        father[fax] = fay;
}
int main() {
    int N,M,v1,v2,oddNum=0;
    cin>>N>>M;
    Init(N);
    for(int i=0; i<M; i++) {
        cin>>v1>>v2;
        Union(v1,v2);
        graph[v1][v2]=1;
        graph[v2][v1]=1;
    }
    for(int i=1; i<=N; i++) {
        int degree=0;
        for(int j=1; j<=N; j++) {
            if(i!=j)
                degree+=graph[i][j];
        }
        if(i>1)
            cout<<" ";
        cout<<degree;
        if(degree%2==1)
            oddNum++;
    }
    int fa = Find(1);
    for(int i=2; i<=N; i++) {
        if(Find(i)!=fa) {
            fa=-1;
            break;
        }
    }
    if(fa!=-1) {
        if(oddNum==0)
            cout<<endl<<"Eulerian"<<endl;
        else if(oddNum==2)
            cout<<endl<<"Semi-Eulerian"<<endl;
        else
            cout<<endl<<"Non-Eulerian"<<endl;
    } else {
        cout<<endl<<"Non-Eulerian"<<endl;
    }
    return 0;
}

1127 ZigZagging on a Tree

        这道题是考二叉树遍历的基础题。给出二叉树的中序、后序遍历序列,求它的层序遍历序列(其中层序遍历的规则做了一点修改),解题关键步骤详见代码

void LevelTraverse(TreeNode* root) {
    vector<int> myVec[MAXN];  // 记录每个层次有哪些结点(从左到右按顺序保存)
    queue<TreeNode*> myQueue;
    int maxLevel=1;
    root->level=1;
    myQueue.push(root);
    while(!myQueue.empty()) {
        TreeNode* elem = myQueue.front();
        myQueue.pop();
        int level = elem->level;
        maxLevel = max(maxLevel,level);
        myVec[level].push_back(elem->data);
        if(elem->left!=NULL) {
            elem->left->level = level+1;
            myQueue.push(elem->left);
        }
        if(elem->right!=NULL) {
            elem->right->level = level+1;
            myQueue.push(elem->right);
        }
    }
    cout<<myVec[1][0];
    for(int i=2; i<=maxLevel; i++) {
        if(i%2==0) {
            for(int j=0; j<myVec[i].size(); j++) {
                cout<<" "<<myVec[i][j];
            }
        } else {
            for(int j=myVec[i].size()-1; j>=0; j--) {
                cout<<" "<<myVec[i][j];
            }
        }
    }
    cout<<endl;
}

1128 N Queens Puzzle

就是一道八皇后的基础题,不是很难

1129 Recommendation System

一道中规中矩的模拟题 

#include <iostream>
#include <queue>
using namespace std;
const int MAXN=5e4+10;
struct Item {
    int label;
    int num;
    Item():num(0) {}
    Item(int l):label(l),num(0) {}
    Item(int l,int n):label(l),num(n) {}
    bool operator< (const Item& y) const {
        if(num!=y.num)
            return num < y.num;
        return label > y.label;
    }
};
int kItem[11],epoch=0;    // kItem仅记录榜单前K个物品有哪些,至于这K个物品的具体排名,交给Print函数中的优先队列来计算
int N,K;
int num[MAXN]= {0};
void Print(int newQuery) {
    priority_queue<Item> myQueue;  // 用于按照题目要求的规则进行输出
    for(int i=0; i<epoch; i++) {
        myQueue.push(Item(kItem[i],num[kItem[i]]));
    }
    cout<<newQuery<<":";
    while(!myQueue.empty()) {
        cout<<" "<<myQueue.top().label;
        myQueue.pop();
    }
    cout<<endl;
}
void Fun(int newQuery) {
    Print(newQuery);
    num[newQuery]++;
    int flag=0,minIndex=0;  // minIndex用于记录kItem榜单中,最有可能被newQuery物品替换的物品的下标
    for(int i=0; i<epoch; i++) {
        int tmp1 = kItem[i],tmp2=kItem[minIndex];
        if(num[tmp1]<num[tmp2] || (num[tmp1]==num[tmp2] && tmp1>tmp2))
            minIndex = i;
        if(tmp1==newQuery) {   // 前K个物品中有 newQuery这个物品,无需更新kItem数组
            flag=1;
            break;
        }
    }
    if(flag==0) {   // 前K个物品中无 newQuery这个物品
        if(epoch<K)
            kItem[epoch++] = newQuery;  // 更新kItem数组
        else {
            int tmp = kItem[minIndex];
            if(num[newQuery]>num[tmp] || (num[newQuery]==num[tmp] && newQuery<tmp))
                kItem[minIndex] = newQuery;  // 更新kItem数组
        }
    }
}
int main() {
    int query;
    cin>>N>>K;
    cin>>query;
    num[query]++;
    kItem[epoch++]=query;
    for(int i=1; i<N; i++) {
        cin>>query;
        Fun(query);
    }
    return 0;
}

1130 Infix Expression

就是简单地考二叉树的中序遍历

#include <iostream>
using namespace std;
const int MAXN=21;
struct TreeNode {
    string data;
    int left;
    int right;
    TreeNode():data(""),left(-1),right(-1) {}
} tree[MAXN];
int vis[MAXN]= {0};
string GetExpression(int root) {
    if(root==-1)
        return "";
    else {
        string lpart=GetExpression(tree[root].left);
        string rpart=GetExpression(tree[root].right);
        string part = lpart + tree[root].data + rpart;
        if((tree[root].left!=-1 || tree[root].right!=-1) && vis[root]!=0) {
            part = "("+part+")";
        }
        return part;
    }
}
int main() {
    int N;
    cin>>N;
    for(int i=1; i<=N; i++) {
        cin>>tree[i].data>>tree[i].left>>tree[i].right;
        if(tree[i].left!=-1)
            vis[tree[i].left]=1;
        if(tree[i].right!=-1)
            vis[tree[i].right]=1;
    }
    int root=1;  // 寻找树的根结点
    for(; root<=N; root++)
        if(vis[root]==0)
            break;
    cout<<GetExpression(root)<<endl;
    return 0;
}

1131 Subway Map

题目大意

  • 有N条铁路线路,每个线路经过若干站点;
  • 两个站点若相邻,它们之间的通路只能在一条铁路线路上;地铁站点的编号范围在0000~9999之间;两条线路交叉的站点,称为换乘站点(transfer station),一个换乘站点最多连接5条线路;
  • 有K个查询,对于每个查询,求出从出发站点到目的站点的最优路径;
  • 最优路径的定义是:经过的站点数最少(第一标尺);换乘线路最少(第二标尺)。保证测试数据中,最优路径答案唯一。

解题思路

  • 就是一道最短路径的题目,用Dijkstra算法求解,其中每条边的权值均为1,这样可以获得第一标尺的值;
  • 再用DFS算法,求第二标尺,获得最优路径;
  • 在每条边,加个属性:所属路线,即可;
  • 由于DFS递归得到的路线顺序恰好与原顺序相反,考虑到图是无向图,将出发站点与目的站点调换位置,不影响最终答案,且方便DFS求解最优路径
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
#include <cstring>
using namespace std;
const int MAXN=1e4+1;
struct Edge {
    int to;
    int length;
    int line;  // 表示该边属于哪个地铁线路
    Edge() {}
    Edge(int _to,int _line):to(_to),length(1),line(_line) {}
};
struct Point {
    int number;
    int distance;
    Point() {}
    Point(int n,int d):number(n),distance(d) {}
};
vector<Edge> graph[MAXN];
int dis[MAXN],minTran=INT_MAX;
string resPath="";
vector<Edge> pre[MAXN];
string Tran(int x) {
    if(x==0)
        return "0";
    string res="";
    while(x>0) {
        char temp = (x%10) + '0';
        res = temp + res;
        x/=10;
    }
    return res;
}
string Tran2(int x) {
    if(x==0)
        return "0000";
    string res="";
    while(x>0) {
        char temp = (x%10) + '0';
        res = temp + res;
        x/=10;
    }
    int len = res.size();
    for(int i=1; i<=4-len; i++)
        res = "0"+res;
    return res;
}
void Dijkstra(int s) {
    dis[s]=0;
    queue<Point> myQueue;
    myQueue.push(Point(s,dis[s]));
    while(!myQueue.empty()) {
        int u = myQueue.front().number;
        myQueue.pop();
        for(int i=0; i<graph[u].size(); i++) {
            int v = graph[u][i].to;
            int d = graph[u][i].length;
            int lineLable = graph[u][i].line;
            if(dis[v]>dis[u]+d) {
                pre[v].clear();
                pre[v].push_back(Edge(u,lineLable));
                dis[v]=dis[u]+d;
                myQueue.push(Point(v,dis[v]));
            } else if(dis[v]==dis[u]+d) {
                pre[v].push_back(Edge(u,lineLable));
            }
        }
    }
}
void DFS(int target,int cur,int transfer,int nowline,string path) {
    if(target==cur) {
        if(transfer<minTran) {
            minTran = transfer;
            resPath = path + Tran2(cur) + ".\n";
        }
    } else { // 此处的判断用于剪枝
        for(int i=0; i<pre[cur].size(); i++) {
            int t;
            if(nowline==-1 || nowline==pre[cur][i].line)
                t = transfer;
            else
                t = transfer + 1;
            string p;
            if(path=="") {
                p = "Take Line#"+Tran(pre[cur][i].line)+" from "+Tran2(cur)+" to ";
            } else {
                p = path;
                if(t==transfer+1) {
                    p = p + Tran2(cur) + ".\nTake Line#"+Tran(pre[cur][i].line)+" from "+Tran2(cur)+" to ";
                }
            }
            DFS(target,pre[cur][i].to,t,pre[cur][i].line,p);
        }
    }
}
int main() {
    int N,K,M,s1,s2;
    memset(pre,0,sizeof(pre));
    memset(graph,0,sizeof(graph));
    cin>>N;
    for(int i=1; i<=N; i++) {
        cin>>M>>s1;
        for(int j=1; j<M; j++) {
            cin>>s2;
            graph[s1].push_back(Edge(s2,i));
            graph[s2].push_back(Edge(s1,i));
            s1 = s2;
        }
    }
    cin>>K;
    for(int i=0; i<K; i++) {
        cin>>s1>>s2;
        memset(pre,0,sizeof(pre));
        fill(dis,dis+MAXN,INT_MAX);
        resPath="";
        minTran=INT_MAX;
        Dijkstra(s2);
        DFS(s2,s1,0,-1,"");
        cout<<dis[s1]<<endl<<resPath;
    }
    return 0;
}

1132 Cut Integer

#include <iostream>
using namespace std;
long long Tran(string x) {
    long long res = 0,len = x.size();
    for(int i=0; i<len; i++) {
        res*=10;
        res+=x[i]-'0';
    }
    return res;
}
int main() {
    int N;
    string str,a,b;
    cin>>N;
    for(int i=0; i<N; i++) {
        cin>>str;
        int len = str.size();
        a = str.substr(0,len/2);
        b = str.substr(len/2);
        int elem1=Tran(str),elem2=(Tran(a)*Tran(b));
        if(elem2!=0&&elem1%elem2==0)
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }
    return 0;
}

1133 Splitting A Linked List

题目大意:给一个含N个元素的链表和一个数K,需要进行一些操作,让负数排在非负数前面,让[0, K]区间的数排在大于K的数的前面,并且维持它们在原来链表中的顺序。

解题思路

  • 用静态链表的形式存储链表
  • 将链表分成(负无穷, 0)、[0, K]、(K, 正无穷)三个区间来考虑,针对每个区间设置一个队列,用来维护链表每个元素的最初顺序(利用队列的先进先出的性质)
  • 遍历链表,将将链表元素分别放入相应的队列
  • 设置一定的输出规则,依次输出队列存放的值,即可得到对应的答案
#include <iostream>
#include <climits>
#include <cstdio>
#include <queue>
using namespace std;
const int MAXN=1e5+10;
int K;
struct Node {
    int addr;
    int data;
    int next;
    Node():addr(-1),data(INT_MAX),next(-1) {}
} nodes[MAXN];
int main() {
    int head,N,addr;
    cin>>head>>N>>K;
    for(int i=1; i<=N; i++) {
        cin>>addr;
        cin>>nodes[addr].data>>nodes[addr].next;
        nodes[addr].addr = addr;
    }
    queue<Node> left;
    queue<Node> mid;
    queue<Node> right;
    int cur = head;
    do {
        if(nodes[cur].data<0)
            left.push(nodes[cur]);
        else if(nodes[cur].data<=K)
            mid.push(nodes[cur]);
        else
            right.push(nodes[cur]);
        cur = nodes[cur].next;
    } while(cur!=-1);
    int flag=0;
    while(!left.empty()) {
        Node u = left.front();
        left.pop();
        if(flag==0)
            flag=1;
        else
            printf("%05d\n",u.addr);
        printf("%05d %d ",u.addr,u.data);
    }
    while(!mid.empty()) {
        Node u = mid.front();
        mid.pop();
        if(flag==0)
            flag=1;
        else
            printf("%05d\n",u.addr);
        printf("%05d %d ",u.addr,u.data);
    }
    while(!right.empty()) {
        Node u = right.front();
        right.pop();
        if(flag==0)
            flag=1;
        else
            printf("%05d\n",u.addr);
        printf("%05d %d ",u.addr,u.data);
    }
    printf("-1\n");
    return 0;
}

1134 Vertex Cover

题目大意:给定一个图,在给一个点集,若图的每条边至少有一个点是来自于这个点集,则称这个点集为Vertex Cover。

#include <iostream>
#include <vector>
#include <set>
using namespace std;
const int MAXN=1e4+10;
struct Edge {
    int v1;
    int v2;
    Edge() {}
    Edge(int _v1,int _v2):v1(_v1),v2(_v2) {}
};
int main() {
    int N,M,K,v1,v2,num;
    cin>>N>>M;
    vector<Edge> myVector;
    for(int i=0; i<M; i++) {
        cin>>v1>>v2;
        myVector.push_back(Edge(v1,v2));
    }
    cin>>K;
    for(int i=0; i<K; i++) {
        set<int> mySet;
        cin>>num;
        for(int i=0; i<num; i++) {
            cin>>v2;
            mySet.insert(v2);
        }
        int flag=1;
        for(int j=0; j<M; j++) {
            if(mySet.find(myVector[j].v1)==mySet.end()&&
                    mySet.find(myVector[j].v2)==mySet.end()) {
                flag=0;
                break;
            }
        }
        if(flag==1)
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }
    return 0;
}

1135 Is It A Red-Black Tree(重要)

题目大意:给定一棵二叉查找树(即给出前序序列,中序序列是前序序列绝对值从小到大排序的结果;由前序、中序序列,确定唯一的一棵二叉查找树),判断它是否是一棵合法的红黑树。红黑树有如下五个性质:

  1. 每个结点不是红色就是黑色(本题中,用正的值表示黑色,用负的值表示红色)
  2. 根节点是黑色的
  3. 每一个叶子结点(即空结点)是黑色的
  4. 如果一个结点是红色的,它的孩子结点必须都是黑色的
  5. 从一个结点,到它的所有叶子结点的所有路径中,经过的黑结点的个数是相等的

解题思路

        本题首先要根据先序、中序序列,把对应的二叉树建立起来(这一步是传统操作,难度不大)。然后进行红黑树的判断:

  • 性质1好判断,只要输入数据中不出现0,就不违背性质1。
  • 性质2也好判断,只要树创建好后,对返回的根结点判断即可。
  • 性质3不需要判断,它只是用来确定经过的黑色结点的个数
  • 剩下的性质4和5,很明显需要递归遍历整棵树,具体判断的实现请看代码中的DFS函数
#include <iostream>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
const int MAXN=50;
struct TreeNode {
    int data;
    int black;  // 记录当前结点到根节点经过的黑色结点的个数
    TreeNode* left;  // 左孩子
    TreeNode* right;  // 右孩子
    TreeNode(int d):data(d),left(NULL),right(NULL) {}
};
int PreOrder[MAXN],InOrder[MAXN];
TreeNode* Create(int prel,int prer,int inl,int inr) {
    if(prel>prer)
        return NULL;
    else {
        TreeNode* root = new TreeNode(PreOrder[prel]);    // 先序序列第一个值对应树的根结点
        int pos = inl;  // pos把中序序列分成左右两部分
        for(; pos<=inr; pos++) {
            if(InOrder[pos]==abs(root->data))
                break;
        }
        if(pos<=inr) {  // 这里的判断一定要加上,不然会出现段错误
            int leftLen = pos-inl;   // 记录左子树有多少个结点
            root->left = Create(prel+1,prel+leftLen,inl,pos-1);
            root->right = Create(prel+leftLen+1,prer,pos+1,inr);
        }
        return root;
    }
}
bool DFS(TreeNode* root) { // 用来获取每个结点到叶结点之间,路径上有多少黑色结点
    if(root!=NULL) {
        int ln,rn;  // ln、rn分别表示左子树、右子树到达根结点经过的黑色结点个数
        // 1. 先确定ln和rn的值
        if(root->left==NULL)
            ln=1;
        else {
            if(root->data<0 && root->left->data<0)  // 表示出现了父子结点均为红色的情况,不满足红黑树的第4条性质
                return false;
            if(!DFS(root->left))
                return false;
            ln = root->left->black;
        }
        if(root->right==NULL)
            rn=1;
        else {
            if(root->data<0 && root->right->data<0)  // 表示出现了父子结点均为红色的情况,不满足红黑树的第4条性质
                return false;
            if(!DFS(root->right))
                return false;
            rn = root->right->black;
        }
        // 2. 判断ln和rn的值是否相等,若不等,则违背红黑树第5条性质
        if(ln!=rn)
            return false;
        // 3. 更新根节点的black值
        if(root->data>0)
            root->black = ln + 1;
        else
            root->black = ln;
    }
    return true;
}
int main() {
    int K,N;
    cin>>K;
    for(int i=0; i<K; i++) {
        cin>>N;
        int flag=1;
        for(int j=0; j<N; j++) {
            cin>>PreOrder[j];
            InOrder[j]=abs(PreOrder[j]);
            if(PreOrder[j]==0)
                flag=0;
        }
        if(flag==0) {  // 0无法确定结点是红色还是黑色,违背红黑树第1条性质
            cout<<"No"<<endl;
            continue;
        }
        sort(InOrder,InOrder+N);
        TreeNode* root = Create(0,N-1,0,N-1);
        if(root->data>0 && DFS(root))  // 根节点的值若不为正,则不是黑色,违背红黑树第2条性质
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }
    return 0;
}

1136 A Delayed Palindrome

就是一道基础的字符串操作题。

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
string Add(string x,string y) {  // 这里两个字符串长度相同
    string res="";
    int carry=0;
    for(int i=x.size()-1; i>=0; i--) {
        carry+=(x[i]-'0')+(y[i]-'0');
        char temp = carry%10+'0';
        res = temp + res;
        carry/=10;
    }
    while(carry>0) {
        char temp = carry%10+'0';
        res = temp + res;
        carry/=10;
    }
    int pos = 0;
    while(pos<res.size()&&res[pos]=='0')
        pos++;
    return res.substr(pos);
}
string Reverse(string x) {
    reverse(x.begin(),x.end());
    return x;
}
bool Jud(string x) {
    return x==Reverse(x);
}
int main() {
    string str,str1,str2,res="";
    cin>>str;
    int epoch=0;
    while(Jud(str)==false && epoch<10) {
        epoch++;
        str1 = Reverse(str);
        str2 = Add(str,str1);
        res+=(str+" + "+str1+" = "+str2+"\n");
        str = str2;
    }
    if(Jud(str))
        cout<<res<<str<<" is a palindromic number."<<endl;
    else
        cout<<res<<"Not found in 10 iterations."<<endl;
    return 0;
}

1137 Final Grading

一道简单的模拟题

#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
const int MAXN=3e4+10;
struct Student {
    string id;
    int Gp;
    int Gmid;
    int Gfinal;
    int G;
    int qualified;
    Student():Gp(-1),Gmid(-1),Gfinal(-1),G(-1) {}
    void Calculate() {
        if(Gp<200)
            qualified=-1;
        else {
            if(Gmid<Gfinal)
                G = Gfinal*10;
            else
                G = Gfinal*6 + Gmid*4;
            if(G%10>4)
                G+=10;
            G/=10;
            if(G<60)
                qualified=-1;
            else
                qualified=1;
        }
    }
    bool operator< (const Student& y) const {
        if(qualified!=y.qualified)
            return qualified > y.qualified;
        if(G!=y.G)
            return G > y.G;
        return id < y.id;
    }
} students[MAXN];
int main() {
    map<string,int> myMap1;
    int P,M,N,score,index=0;
    string id;
    cin>>P>>M>>N;
    for(int i=0; i<P; i++) {
        cin>>id>>score;
        if(myMap1.find(id)==myMap1.end())
            myMap1[id] = index++;
        students[myMap1[id]].Gp = score;
        students[myMap1[id]].id = id;
    }
    for(int i=0; i<M; i++) {
        cin>>id>>score;
        if(myMap1.find(id)==myMap1.end())
            myMap1[id] = index++;
        students[myMap1[id]].Gmid = score;
    }
    for(int i=0; i<N; i++) {
        cin>>id>>score;
        if(myMap1.find(id)==myMap1.end())
            myMap1[id] = index++;
        students[myMap1[id]].Gfinal = score;
    }
    for(int i=0; i<index; i++)
        students[i].Calculate();
    sort(students,students+index);
    for(int i=0; i<index; i++) {
        if(students[i].qualified==-1)
            break;
        cout<<students[i].id<<" "<<students[i].Gp<<" "<<students[i].Gmid<<" "<<students[i].Gfinal<<" "<<students[i].G<<endl;
    }
    return 0;
}

1138 Postorder Traversal

题目大意:给定先序、中序遍历序列,求后序序列的第一个元素

解题思路

  • 题目不需要求出整个后序序列,以往习惯做法是根据先序、中序建树,然后后序遍历得出结果。不过这题数据规模比较大,如果这样做的话,会超时的。
  • 可以在建树的过程中,找到题目需要的答案,一旦找到,就停止建树过程,马上返回结果,这样就减少不必要的耗时。关键代码如下:
int flag=-1;
TreeNode* Create(int prel,int prer,int inl,int inr) {
    if(flag==1 || prel>prer)
        return NULL;
    else {
        TreeNode* root = new TreeNode(PreOrder[prel]);
        if(prel==prer) {
            flag=1;
            cout<<root->data<<endl;
        }
        int pos=inl;
        for(; pos<=inr; pos++) {
            if(InOrder[pos]==root->data)
                break;
        }
        int leftLen = pos-inl;
        root->left = Create(prel+1,prel+leftLen,inl,pos-1);
        root->right = Create(prel+leftLen+1,prer,pos+1,inr);
        return root;
    }
}

1139 First Contact(重要)

题目大意:给出好友关系图,再给出K对lovers。需要帮助每对lovers,寻找相应的好友,来间接(不那么直接地)传递他们的爱意。

解题思路

  • 这道题就是模拟+排序。
  • 输入数据的处理方面很容易忽略-0000与0000的区别,解决办法详见代码。
  • 寻找好友的过程可以用三重循环来实现。
  • 用结构体的重载小于号函数+优先队列,可以实现多对好友的排序输出。
#include <iostream>
#include <vector>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
const int MAXN=1e4+10;
vector<int> graph[MAXN];   // 用邻接表存储好友关系
int gender[MAXN]= {0};
struct Pair {   // 该结构体用于把结果按要求的格式和顺序输出,小于号的重载函数是主要的部分
    int from;
    int to;
    Pair(int _from,int _to):from(_from),to(_to) {}
    bool operator< (const Pair& y) const {
        if(from!=y.from)
            return from > y.from;
        return to > y.to;
    }
};
int Tran(string str) {  // 将数字从字符串类型转换为int类型
    if(str[0]=='-')
        str = str.substr(1);
    int res = 0;
    for(int i=0; i<str.size(); i++) {
        res*=10;
        res+=(str[i]-'0');
    }
    return res;
}
int GetGender(string x) {   // 获取对应的性别:-1表示女,1表示男
    if(x[0]=='-')
        return -1;
    return 1;
}
void setEdge(int x,int y) {   // 将好友关系更新到邻接表中
    graph[x].push_back(y);
    graph[y].push_back(x);
}
void Calculate(int a,int b) {
    if(gender[a]==0 || gender[b]==0) {   // a和b至少有一个不存在于好友关系表graph中
        cout<<0<<endl;
        return ;
    }
    priority_queue<Pair> res;   // 用优先队列,配合结构体的小于号重载函数,能实现自动排序的功能,从而控制输出的格式
    for(int i=0; i<graph[a].size(); i++) {  // 该三重循环函数,就是寻找合适好友的关键代码
        int u = graph[a][i];
        if(u!=a && u!=b && gender[u]==gender[a]) {
            for(int j=0; j<graph[u].size(); j++) {
                int v = graph[u][j];
                if(v!=a && v!=b && gender[v]==gender[b]) {
                    for(int k=0; k<graph[v].size(); k++) {
                        if(graph[v][k]==b)
                            res.push(Pair(u,v));
                    }
                }
            }
        }
    }
    // 以下是结果的输出
    cout<<res.size()<<endl;
    while(!res.empty()) {
        Pair x = res.top();
        res.pop();
        printf("%04d %04d\n",x.from,x.to);
    }
}
int main() {
    int N,M,K;
    cin>>N>>M;
    for(int m=0; m<M; m++) {
        string str1,str2;  // 增加了string到int的转换流程,就是为了发现-0000与0000这个边界情况!!!!!
        int v1,v2;
        cin>>str1>>str2;
        v1 = Tran(str1);
        v2 = Tran(str2);
        gender[v1] = GetGender(str1);
        gender[v2] = GetGender(str2);
        setEdge(v1,v2);
    }
    cin>>K;
    for(int k=0; k<K; k++) {
        int a,b;
        cin>>a>>b;
        Calculate(abs(a),abs(b));  // 以上都是数据保存过程中的处理,代码核心功能在这个函数
    }
    return 0;
}

1140 Look-and-say Sequence

一道规律发现题,思路详见代码

#include <iostream>
using namespace std;
string Tran(int x) {   // 将数字转换为对应的字符串形式
    if(x==0)
        return "0";
    string res="";
    while(x>0) {
        char temp = (x%10) + '0';
        res = temp + res;
        x/=10;
    }
    return res;
}
string GenerateNext(string old) {
    string res = "";
    int len = old.size();
    for(int i=0; i<len;) {
        int pre=i;
        while(i<len&&old[i]==old[pre])
            i++;
        res+=old[pre]+Tran(i-pre);  // 每遇到一个新字符,就记录它的值、记录它连续的个数,拼接到新的字符串中
    }
    return res;
}
int main() {
    int D,N;
    cin>>D>>N;
    string res = Tran(D);
    for(int i=1; i<N; i++)
        res = GenerateNext(res);
    cout<<res<<endl;
    return 0;
}

1141 PAT Ranking of Institutions

题目大意:给出一组来自不同学校的学生的考试成绩,求各个学校的排名。

解题思路

  1. 设计好学校结构体的属性和函数(先用sa sb st三个变量保存三个不同考试级别的分数总和;再设计函数GetTWS(),计算TWS,保留整数部分而不是四舍五入)
  2. 处理输入数据:学生的学号不用保存,它只是用来判断学生所得到的成绩对应哪一个级别的考试;学生归属的学校的名字,要用函数ToLower()来处理,这个函数可以记下来,以后需要的时候直接用,还是比较好用的
  3. 计算每个学校的TWS结果
  4. 根据题目的排序规则设计好Institution结构体的小于号重载函数,然后调用sort函数
  5. 输出排名结果:注意一下rank的取值规则即可
#include <iostream>
#include <string>
#include <algorithm>
#include <map>
using namespace std;
const int MAXN=1e5+10;
struct Institution {
    string school;
    int sa;
    int sb;
    int st;
    int TWS;
    int stuNum;
    Institution():sa(0),sb(0),st(0),stuNum(0) {}
    void GetTWS() {
        double res = sa;
        res+=sb/1.50+st*1.5;
        TWS=res;
    }
    bool operator< (const Institution& y) const {
        if(TWS!=y.TWS)
            return TWS > y.TWS;
        if(stuNum!=y.stuNum)
            return stuNum<y.stuNum;
        return school < y.school;
    }
} institution[MAXN];
map<string,int> ID2num;
map<int,string> num2ID;
string ToLower(string x) {
    transform(x.begin(),x.end(),x.begin(),::tolower);
    return x;
}
int main() {
    int N,score;
    string ID,SchoolName;
    cin>>N;
    for(int i=0; i<N; i++) {
        cin>>ID>>score>>SchoolName;
        SchoolName = ToLower(SchoolName);
        if(ID2num.find(SchoolName)==ID2num.end()) {
            int len = ID2num.size();
            ID2num[SchoolName]=len;
        }
        institution[ID2num[SchoolName]].school = SchoolName;
        institution[ID2num[SchoolName]].stuNum++;
        if(ID[0]=='A')
            institution[ID2num[SchoolName]].sa+=score;
        else if(ID[0]=='B')
            institution[ID2num[SchoolName]].sb+=score;
        else if(ID[0]=='T')
            institution[ID2num[SchoolName]].st+=score;
    }
    int length = ID2num.size(),rankPre=1;
    for(int i=0; i<length; i++)
        institution[i].GetTWS();
    sort(institution,institution+length);
    cout<<length<<endl;
    for(int i=0; i<length; i++) {
        Institution elem = institution[i];
        if(!(i==0 || elem.TWS==institution[i-1].TWS))
            rankPre = i+1;
        cout<<rankPre<<" "<<elem.school<<" "<<elem.TWS<<" "<<elem.stuNum<<endl;
    }
    return 0;
}

1142 Maximal Clique(不知道为什么这题代码一直未通过一个测试点)

题目大意

  1. 给出一个无向图,在给出K个点集。
  2. 为每个点集,判断集合中每个点是否两两都是直接相连的,如果是,则是一个Clique;反之则不是。 
  3. 若是一个Clique,则再判断“是否能往集合中添加一个点,该点与集合中其他点均直接相连”,如果能,则不是一个max Clique;反之则是。

解题思路:(详见代码,不是很难)

这篇博文的代码是通过了测试的。下面的代码是我自己写的,没通过测试。我比对了半天,没找出我代码的bug,太难受了,堵心。

#include <iostream>
using namespace std;
const int MAXN=201;
int graph[MAXN][MAXN]= {0};
int temp[MAXN];  // 记录待判断的子图
int vis[MAXN]= {0};   // 记录哪些结点被包含在子图中
int N,Ne,M,K;
int main() {
    int x, y;
    cin>>N>>Ne;
    for(int i=0; i<Ne; i++) {
        cin>>x>>y;
        graph[x][y]=1;
        graph[y][x]=1;
    }
    cin>>M;
    for(int query=1; query<=M; query++) {
        cin>>K;
        fill(vis,vis+N,0);
        for(int i=0; i<K; i++)
            cin>>temp[i],vis[temp[i]]=1;
        int flag=1;
        for(int i=0; i<K; i++) {
            for(int j=i+1; j<K&&flag==1; j++) {
                if(graph[temp[i]][temp[j]]==0) {  // 存在不连通的情况
                    flag=0;
                    break;
                }
            }
        }
        if(flag==0) {
            cout<<"Not a Clique"<<endl;
        } else {
            int jud=1;
            for(int i=1; i<=N; i++) {
                if(vis[i]==0) {
                    for(int j=0; j<K; j++) {
                        if(graph[temp[j]][i]==0) {  // 存在不连通的情况
                            break;
                        }
                        if(j==K-1) {
                            jud=0;
                        }
                    }
                    if(jud==0) {
                        cout<<"Not Maximal"<<endl;
                        break;
                    }
                }
            }
            if(jud==1)
                cout<<"Yes"<<endl;
        }
    }
    return 0;
}

1143 Lowest Common Ancestor

题目大意:给定一棵二叉搜索树,再给两个结点值,查询这两个结点的最近公共祖先。

解题思路

  1. 先根据题目给出的先序遍历序列,计算中序遍历序列(即先序序列从小到大排序的结果),然后根据这两个序列,创建对应的二叉搜索树。
  2. 然后利用DFS函数分别获得两个结点的路径,若结点不在树中,DFS函数返回false。
  3. 然后根据这两个路径,用if...else分支结构判断各种可能的情况并输出对应格式的结果。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN=1e4+10;
struct TreeNode {
    int data;
    TreeNode* left;
    TreeNode* right;
    TreeNode():left(NULL),right(NULL) {}
    TreeNode(int d):data(d),left(NULL),right(NULL) {}
};
int PreOrder[MAXN],InOrder[MAXN];
TreeNode* Create(int prel,int prer,int inl,int inr) {
    if(prel>prer)
        return NULL;
    else {
        TreeNode* root = new TreeNode(PreOrder[prel]);
        int pos = inl;
        for(; pos<=inr; pos++) {
            if(InOrder[pos]==root->data)
                break;
        }
        int leftLen = pos-inl;
        root->left = Create(prel+1,prel+leftLen,inl,pos-1);
        root->right = Create(prel+leftLen+1,prer,pos+1,inr);
        return root;
    }
}
bool DFS(TreeNode* root,int val,vector<int>& path) {
    if(root==NULL)
        return false;
    path.push_back(root->data);
    if(root->data == val)
        return true;
    else if(root->data < val)
        return DFS(root->right,val,path);
    else if(root->data > val)
        return DFS(root->left,val,path);
}
void Calculate(TreeNode* root,int u,int v) {
    vector<int> upath;
    vector<int> vpath;
    bool judu = DFS(root,u,upath);
    bool judv = DFS(root,v,vpath);
    if(judu==false && judv==false)
        cout<<"ERROR: "<<u<<" and "<<v<<" are not found."<<endl;
    else if(judu==false)
        cout<<"ERROR: "<<u<<" is not found."<<endl;
    else if(judv==false)
        cout<<"ERROR: "<<v<<" is not found."<<endl;
    else {
        int lenu = upath.size();
        int lenv = vpath.size();
        int pos=0;
        for(; pos<lenu&&pos<lenv; pos++) {
            if(upath[pos]!=vpath[pos])
                break;
        }
        if(pos==lenu)
            cout<<u<<" is an ancestor of "<<v<<"."<<endl;
        else if(pos==lenv)
            cout<<v<<" is an ancestor of "<<u<<"."<<endl;
        else
            cout<<"LCA of "<<u<<" and "<<v<<" is "<<upath[pos-1]<<"."<<endl;
    }
}
int main() {
    int M,N,u,v;
    cin>>M>>N;
    for(int i=0; i<N; i++) {
        cin>>PreOrder[i];
        InOrder[i] = PreOrder[i];
    }
    sort(InOrder,InOrder+N);
    TreeNode* root = Create(0,N-1,0,N-1);
    for(int m=0; m<M; m++) {
        cin>>u>>v;
        Calculate(root,u,v);
    }
    return 0;
}

1144 The Missing Number

题目大意:给出N个int范围内的整数列表(可正可负),求最小的正整数,且该正整数不在列表中

解题思路:详见代码

其他

PAT上的测试,可能有个没考虑到:

10

7 7 7 7 6 5 4 3 2 1 

#include <iostream>
#include <queue>
using namespace std;
int main() {
    int N,elem;
    priority_queue<int,vector<int>,greater<int> > myQueue;  // 将列表正整数从小到大排列
    cin>>N;
    for(int i=0; i<N; i++) {
        cin>>elem;
        if(elem>0)  // 解题中,我们只需要关心正整数
            myQueue.push(elem);
    }
    int flag=0;
    int index=1;  // index表示当前队列未遍历到的最小正整数
    while(!myQueue.empty()) {
        if(myQueue.top()>index) {  // 队列不存在index
            flag=1;
            cout<<index<<endl;
            break;
        } else if(myQueue.top()==index)  // 此处若myQueue.top()<index,说明队首元素之前已经出现过了,index因而不做任何处理
            index++;
        myQueue.pop();
    }
    if(flag==0)  // 出现这种情况说明,myQueue中的元素均能完整覆盖区间[1, index-1]
        cout<<index;   // 如果这里输出myQueue.size()的值,题目好像不会检测出错误,但是它是一个错误
    return 0;
}

1145 Hashing - Average Search Time(考点在hash)

题目大意:将N个数插入到一个给定大小的hash表中(用正向平方探测法处理冲突)。在给出M个数,查询这M个数是否在hash表格中存在,求这M个数的平均查询次数。

解题思路

  • 平方探测法的使用。在平方探测法中,为了尽可能避免扎堆现象,当表中下标为H(key)的位置被占时,将按下面的顺序检查表中的位置:H(key)+1^2、H(key)+2^2、H(key)+3^2......如果检查过程中H(key)+k^2超过了表长TSize,那么就对其取模(模数为TSize)。如果k在[0,TSize)范围内都无法找到地址,那么当k>=TSize时也肯定无法找到,因此只需遍历k从[0, TSize-1]即可。
  • 查询操作。查询当前数在hash表中所在位置或是计算查询次数,均需要再次模拟插入过程,当找到这个数,或者发现这个位置上没有数(即值为-1)时,表示已经找到这个数或者无此数,无需继续查询。
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN=1e4+10;
int TSize,MSize,N,M;
int Hash[MAXN];
void Init() { // 确定hash表格的大小
    for(int i=MSize; i<MAXN; i++) {
        int flag=1;
        for(int j=2; j*j<=i && flag==1; j++)
            if(i%j==0)
                flag=0;
        if(flag==1) {
            TSize = i;
            break;
        }
    }
    fill(Hash,Hash+TSize+1,-1);
}
void SetHashKey(int key) {
    int pos = key%TSize,flag=0;
    for(int i=0; i<TSize; i++) {   // 二次探测的边界在区间[0,TSize-1]
        if(Hash[(i*i+pos)%TSize]==-1) {
            Hash[(i*i+pos)%TSize] = key;
            flag=1;
            break;
        }
    }
    if(flag==0)
        cout<<key<<" cannot be inserted."<<endl;
}
int GetSearchTime(int val) {
    int i,res=0,pos = val%TSize;
    for(i=0; i<=TSize; i++) {
        res++;  // 比较次数加1
        if(Hash[(i*i+pos)%TSize]==-1 || Hash[(i*i+pos)%TSize]==val)
            break;
    }
    return res;
}
int main() {
    cin>>MSize>>N>>M;
    Init();
    for(int i=0; i<N; i++) {
        int elem;
        cin>>elem;
        SetHashKey(elem);
    }
    int sum=0;
    for(int m=0; m<M; m++) {
        int elem;
        cin>>elem;
        sum+=GetSearchTime(elem);
    }
    printf("%.1lf\n",sum*1.0/M);
    return 0;
}

1146 Topological Order

题目大意:给定一个有向图,给出K个序列,判断其中哪些序列是该图的拓扑路径。

解题思路:判断是否是拓扑路径,只需按着给出的路径走,对每个点执行以下操作:

  1. 该点入度是否为0?若为0,则执行后续操作;若不为0,则不是拓扑路径,算法结束判断。
  2. 遍历该点的所有出边,将每条边对应的另一端点的入度减1。
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int MAXN=1e3+10;
vector<int> graph[MAXN];
int inDegree[MAXN];
int tmpDegree[MAXN],tmpTopo[MAXN];
bool Jud(int n) {
    for(int i=1; i<=n; i++)
        tmpDegree[i] = inDegree[i];
    for(int i=1; i<=n; i++) {
        int u = tmpTopo[i];
        if(tmpDegree[u]!=0)
            return false;
        for(int j=0; j<graph[u].size(); j++) {
            int v = graph[u][j];
            tmpDegree[v]--;
        }
    }
    return true;
}
int main() {
    int N,M,K;
    cin>>N>>M;
    for(int i=0; i<M; i++) {
        int v1,v2;
        cin>>v1>>v2;
        graph[v1].push_back(v2);
        inDegree[v2]++;
    }
    cin>>K;
    int printFlag=0;
    for(int k=0; k<K; k++) {
        for(int i=1; i<=N; i++)
            cin>>tmpTopo[i];
        if(!Jud(N)) {
            if(printFlag==0)
                printFlag=1;
            else
                cout<<" ";
            cout<<k;
        }
    }
    cout<<endl;
    return 0;
}

1147 Heaps

题目大意:给定一个完全二叉树的层序遍历序列,判断它是否是一个大顶堆或者是一个小顶堆,或者不是一个堆,并输出该树的后序遍历序列。

解题思路

完全二叉树可以直接用数组来存储,因为第i个结点的左、右孩子分别是第i*2、i*2+1个结点。

具体思路详见代码

#include <iostream>
using namespace std;
const int MAXN=1e4+10;
int tree[MAXN],postFlag;
void PostTraverse(int cur,int n) {
    if(cur<=n) {
        PostTraverse(cur*2,n);
        PostTraverse(cur*2+1,n);
        if(postFlag==0)
            postFlag=1;
        else
            cout<<" ";
        cout<<tree[cur];
    }
}
int main() {
    int M,N;
    cin>>M>>N;
    for(int m=0; m<M; m++) {
        int flag=0;  //  0 表示父子结点目前的值是相等的,1表示小顶堆,2表示大顶堆,-1表示不是堆
        for(int n=1; n<=N; n++) {
            cin>>tree[n];
            if(flag==-1)
                continue;
            if(n>1) {
                if(tree[n/2]>tree[n]) { // 可能是大顶堆
                    if(flag==0)
                        flag=2;
                    else if(flag==1)
                        flag=-1;
                } else if(tree[n/2]<tree[n]) { // 可能是小顶堆
                    if(flag==0)
                        flag=1;
                    else if(flag==2)
                        flag=-1;
                }
            }
        }
        if(flag==-1)
            cout<<"Not Heap"<<endl;
        else if(flag==1)
            cout<<"Min Heap"<<endl;
        else if(flag==2)
            cout<<"Max Heap"<<endl;
        postFlag=0;
        PostTraverse(1,N);
        cout<<endl;
    }
    return 0;
}

1148 Werewolf - Simple Version

题目大意:在狼人杀游戏中,有N个玩家,玩家分为人类、狼两大阵营,其中有且仅有2只狼。N个玩家每人分别叙述一条信息,其中有且仅有2个玩家说谎(一个是狼,一个是人类)。任务:找出2只狼。

解题思路

  1. 题目的N规模仅为100,不是很大,可以对N个玩家按照递增顺序枚举所有是狼的可能组合(复杂度为O(n^2))
  2. 然后对每个组合判断说谎者的个数
  3. 若个数为2,且说谎者是一个人类和一只狼,则该组合就是答案,输出结果,程序结束,不再进行后续判断
  4. 反之,该组合不是答案,继续枚举下一个组合
  5. 若遍历完所有组合,仍然没有找到答案,则输出"No Solution"
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN=101;
int N;
int desc[MAXN];
bool solve(int p1,int p2) {
    vector<int> liars;
    for(int i=1; i<=N; i++) {
        if((desc[i]<0 && abs(desc[i])!=p1 && abs(desc[i])!=p2)||
                (desc[i]>0 && (desc[i]==p1 || desc[i]==p2))) {   // 当前玩家在说谎
            liars.push_back(i);
        }
        if(liars.size()>2)
            return false;
    }
    if(liars.size()==2) { // 要记得判断:说谎者必须1个是狼,1个是人
        if((liars[0]==p1||liars[0]==p2)&&liars[1]!=p1&&liars[1]!=p2)
            return true;
        if((liars[1]==p1||liars[1]==p2)&&liars[0]!=p1&&liars[0]!=p2)
            return true;
    }
    return false;
}
int main() {
    cin>>N;
    for(int i=1; i<=N; i++)
        cin>>desc[i];
    int flag=0;
    for(int i=1; i<N&&flag==0; i++) {
        for(int j=i+1; j<=N; j++) {
            if(solve(i,j)==true) {
                flag=1;
                cout<<i<<" "<<j<<endl;
                break;
            }
        }
    }
    if(flag==0) {
        cout<<"No Solution"<<endl;
    }
    return 0;
}

1149 Dangerous Goods Packaging

题目大意:给出N对不兼容的物品,在给出K个物品清单。对每个清单,判断它的所有物品是否可以兼容地放在一个容器中。

解题思路:用set记录每个物品不兼容的其他物品的集合(incompa数组可以类比vector<int> graph[MAXN],只不过我们不需要关心具体下标,只要元素存在于集合中就好,所以用set代替vector)

#include <iostream>
#include <set>
using namespace std;
const int MAXN=1e5+10;
set<int> incompa[MAXN];
int arr[1001];
int main() {
    int N,M,K,v1,v2;
    cin>>N>>M;
    for(int i=0; i<N; i++) {
        cin>>v1>>v2;
        incompa[v1].insert(v2);
        incompa[v2].insert(v1);
    }
    for(int m=0; m<M; m++) {
        int flag=1;
        cin>>K;
        for(int k=0; k<K; k++) {
            cin>>arr[k];
            for(int i=0; i<k&& flag==1; i++) {
                if(incompa[arr[k]].find(arr[i])!=incompa[arr[k]].end()) {
                    flag=0;
                }
            }
        }
        if(flag==1)
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }
    return 0;
}

1150 Travelling Salesman Problem

题目大意:给定一个无向图,再给出K个路径,对每个路径:求出长度,并判断是否经过图中所有点并形成环路。

解题思路:比较简单,详见代码

#include <iostream>
#include <vector>
#include <set>
using namespace std;
const int MAXN=201;
int graph[MAXN][MAXN]= {0};
int resk=-1,resdis=-1,N;
void Calculate(int knum,vector<int> path) {
    set<int> vis;
    int totalDis=0,len=path.size();
    for(int i=len-1; i>0; i--) {
        int u = path[i],v = path[i-1];
        if(graph[u][v]!=0) {
            totalDis+=graph[u][v];
            vis.insert(u);
            vis.insert(v);
        } else {
            cout<<"Path "<<knum<<": NA (Not a TS cycle)"<<endl;
            return ;
        }
    }
    string str;
    if(vis.size()==N && path[0]==path[len-1]) {
        if(len==N+1)
            str=" (TS simple cycle)";
        else
            str=" (TS cycle)";
        if(resdis==-1 || totalDis<resdis) {
            resdis = totalDis;
            resk = knum;
        }
    } else {
        str=" (Not a TS cycle)";
    }
    cout<<"Path "<<knum<<": "<<totalDis<<str<<endl;
}
int main() {
    int M,K,c1,c2,dis;
    cin>>N>>M;
    for(int m=0; m<M; m++) {
        cin>>c1>>c2>>dis;
        graph[c1][c2]=dis;
        graph[c2][c1]=dis;
    }
    cin>>K;
    for(int k=1; k<=K; k++) {
        vector<int> p;
        int elem,n;
        cin>>n;
        for(int i=0; i<n; i++) {
            cin>>elem;
            p.push_back(elem);
        }
        Calculate(k,p);
    }
    cout<<"Shortest Dist("<<resk<<") = "<<resdis<<endl;
    return 0;
}

1151 LCA in a Binary Tree

题目大意:给定一颗二叉树的中序、前序序列,求两个结点的最近祖先。

解题思路

  1. 根据中序、前序序列,创建对应的二叉树
  2. 用一个set类型实例(treeKeys)记录树中包含的结点,用于判断待查询的结点是否在树中存在(在复杂度方面,用空间换时间)
  3. 若待查询的一对结点均存在于树中,则求它们的最近祖先
#include <iostream>
#include <vector>
#include <set>
using namespace std;
const int MAXN=10001;
struct TreeNode {
    int data;
    TreeNode* left;
    TreeNode* right;
    TreeNode():left(NULL),right(NULL) {}
    TreeNode(int _data):data(_data),left(NULL),right(NULL) {}
};
int InOrder[MAXN],PreOrder[MAXN],tmpLevel,isFind,level1,level2;
int seq1[MAXN],seq2[MAXN];
set<int> treeKeys;   // 该集合用于判断待查询的key是否在树中
TreeNode* Create(int prel,int prer,int inl,int inr) {  // 前序、中序序列可以唯一地确定一颗树
    if(prel>prer)
        return NULL;
    TreeNode* root = new TreeNode(PreOrder[prel]);
    int pos = inl;
    for(; pos<=inr; pos++) {
        if(InOrder[pos]==root->data)
            break;
    }
    int leftLen = pos-inl;
    root->left = Create(prel+1,prel+leftLen,inl,pos-1);
    root->right = Create(prel+leftLen+1,prer,pos+1,inr);
    return root;
}
void Find(TreeNode* root,int val,int level,int* seq) {
    if(root!=NULL&&isFind==0) {  // 剪枝操作,若已找到目标结点,则结束后面的递归
        seq[level]=root->data;
        if(root->data!=val) {
            Find(root->left,val,level+1,seq);
            Find(root->right,val,level+1,seq);
        } else {   // 找到值为val的目标结点
            tmpLevel=level;
            isFind=1;
        }
    }
}
void LCA(int e1,int e2) {
    int pos=0;
    while(pos<=level1&&pos<=level2&&seq1[pos]==seq2[pos]) {
        pos++;
    }
    if(seq1[pos-1]==e1)
        cout<<e1<<" is an ancestor of "<<e2<<"."<<endl;
    else if(seq2[pos-1]==e2)
        cout<<e2<<" is an ancestor of "<<e1<<"."<<endl;
    else
        cout<<"LCA of "<<e1<<" and "<<e2<<" is "<<seq1[pos-1]<<"."<<endl;
}
int main() {
    int M,N;
    cin>>M>>N;
    for(int i=1; i<=N; i++) {
        cin>>InOrder[i];
        treeKeys.insert(InOrder[i]);
    }
    for(int i=1; i<=N; i++)
        cin>>PreOrder[i];
    TreeNode* root = Create(1,N,1,N);
    for(int m=1; m<=M; m++) {
        int e1,e2;
        cin>>e1>>e2;
        if(treeKeys.count(e1)==0&&treeKeys.count(e2)==0)
            cout<<"ERROR: "<<e1<<" and "<<e2<<" are not found."<<endl;
        else if(treeKeys.count(e1)==0)
            cout<<"ERROR: "<<e1<<" is not found."<<endl;
        else if(treeKeys.count(e2)==0)
            cout<<"ERROR: "<<e2<<" is not found."<<endl;
        else {
            isFind=0;
            Find(root,e1,0,seq1);
            level1=tmpLevel;
            isFind=0;
            Find(root,e2,0,seq2);
            level2=tmpLevel;
            LCA(e1,e2);
        }
    }
    return 0;
}

1152 Google Recruitment

题目大意:在含L个数字的连续序列中,寻找第一个出现的子连续序列,使得该子连续序列长度为K、且组成的数是一个素数。

解题思路:题目比较简单,见代码。

#include <iostream>
using namespace std;
int L,K;
bool isPrime(int x) {
    // 判断素数的时候要把0和1也考虑进去
    if(x==0 || x==1)
        return false;
    for(int i=2; i*i<=x; i++) {
        if(x%i==0)
            return false;
    }
    return true;
}
int Tran(string x) {
    int carry=0,len=x.size();
    for(int i=0; i<len; i++) {
        carry*=10;
        carry+=(x[i]-'0');
    }
    return carry;
}
int main() {
    int flag=0;
    string str;
    cin>>L>>K>>str;
    for(int i=0; i+K<=L; i++) {  // 这里的等号也要取到
        if(isPrime(Tran(str.substr(i,K)))==true) {
            cout<<str.substr(i,K)<<endl;
            flag=1;
            break;
        }
    }
    if(flag==0)
        cout<<404<<endl;
    return 0;
}

1153 Decode Registration Card of PAT(这题目自己暂时还未解决超时的问题)

这篇博文写的很好

#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <unordered_map>
#include <string>
#include <set>
using namespace std;
const int MAXN=1e4+1;
struct Type1 {
    string CardNum;
    int score;
    Type1() {}
    Type1(string _CardNum,int _score):CardNum(_CardNum),score(_score) {}
    bool operator< (const Type1& y) const {
        if(score!=y.score)
            return score > y.score;
        return CardNum < y.CardNum;
    }
};
struct Type2 {
    set<string> Nt;
    int Ns;
    Type2():Ns(0) {}
};
struct Type3 {
    string site;
    set<string> Nt;
    Type3() {}
    Type3(string _site, set<string> nt):site(_site),Nt(nt) {}
    bool operator< (const Type3& y) const {
        if(Nt.size()!=y.Nt.size())
            return Nt.size() > y.Nt.size();
        return site < y.site;
    }
};
vector<Type1> LT,LA,LB;
map<string,Type2> SITE;
unordered_map<string,unordered_map<string,set<string> > > DATE;
void AddType1(string card,int score) {
    if(card[0]=='T')
        LT.push_back(Type1(card,score));
    else if(card[0]=='A')
        LA.push_back(Type1(card,score));
    else if(card[0]=='B')
        LB.push_back(Type1(card,score));
}
void SortType1() {
    sort(LT.begin(),LT.end());
    sort(LA.begin(),LA.end());
    sort(LB.begin(),LB.end());
}
void GetType1(string level) {
    vector<Type1> seq;
    if(level[0]=='T')
        seq = LT;
    else if(level[0]=='A')
        seq = LA;
    else if(level[0]=='B')
        seq = LB;
    int len = seq.size();
    if(len==0)
        cout<<"NA"<<endl;
    else {
        for(int i=0; i<len; i++)
            cout<<seq[i].CardNum<<" "<<seq[i].score<<endl;
    }
}
void AddType2(string card,int score) {
    string site = card.substr(1,3);
    SITE[site].Ns+=score;
    SITE[site].Nt.insert(card.substr(10,3));
}
void GetType2(string site) {
    if(SITE.count(site)==0)
        cout<<"NA"<<endl;
    else
        cout<<SITE[site].Nt.size()<<" "<<SITE[site].Ns<<endl;
}
void AddType3(string card,int score) {
    string date = card.substr(4,6);
    string site = card.substr(1,3);
    DATE[date][site].insert(card.substr(10,3));
}
void GetType3(string date) {
    vector<Type3> res;
    unordered_map<string,set<string> > dateElem = DATE[date];
    if(dateElem.size()==0)
        cout<<"NA"<<endl;
    else {
        unordered_map<string,set<string> >::iterator it=dateElem.begin();
        for(; it!=dateElem.end(); it++) {
            res.push_back(Type3(it->first,it->second));
        }
        sort(res.begin(),res.end());
        int len = res.size();
        for(int i=0; i<len; i++)
            cout<<res[i].site<<" "<<res[i].Nt.size()<<endl;
    }
}
int main() {
    int N,M,score,type;
    string CardNum,term;
    cin>>N>>M;
    for(int n=1; n<=N; n++) {
        cin>>CardNum>>score;
        AddType1(CardNum,score);
        AddType2(CardNum,score);
        AddType3(CardNum,score);
    }
    SortType1();
    for(int m=1; m<=M; m++) {
        cin>>type>>term;
        cout<<"Case "<<m<<": "<<type<<" "<<term<<endl;
        if(type==1)
            GetType1(term);
        else if(type==2)
            GetType2(term);
        else if(type==3)
            GetType3(term);
    }
    return 0;
}

1154 Vertex Coloring

一道模拟题,不是很难

#include <iostream>
#include <set>
#include <vector>
using namespace std;
const int MAXN=1e4+10;
int N,M,color[MAXN];
struct Edge {
    int v1;
    int v2;
    Edge() {}
    Edge(int _v1,int _v2):v1(_v1),v2(_v2) {}
};
vector<Edge> myEdge;
set<int> ColorKinds;
void Calculate() {
    int len = myEdge.size(),flag=1;
    for(int i=0; i<len&&flag==1; i++) {
        int v1=myEdge[i].v1,v2=myEdge[i].v2;
        if(color[v1]==color[v2])
            flag=0;
    }
    if(flag==0)
        cout<<"No"<<endl;
    else
        cout<<ColorKinds.size()<<"-coloring"<<endl;
}
int main() {
    cin>>N>>M;
    for(int m=0; m<M; m++) {
        int v1,v2;
        cin>>v1>>v2;
        myEdge.push_back(Edge(v1,v2));
    }
    int K;
    cin>>K;
    for(int k=0; k<K; k++) {
        ColorKinds.clear();
        for(int i=0; i<N; i++) {
            cin>>color[i];
            ColorKinds.insert(color[i]);
        }
        Calculate();
    }
    return 0;
}

1155 Heap Paths

题目大意:给出一棵完全二叉树的层序遍历序列,判断它是否是小顶堆或大顶堆,并按照右子树优先的顺序输出所有从根结点到叶结点的路径。

解题思路:DFS+字符串处理+完全二叉树的考点,详情见代码

#include <iostream>
#include <set>
#include <sstream>
using namespace std;
const int MAXN=2e3+10;
int tree[MAXN],N;
set<string> resPath;
string Tran(int x) {
    stringstream ss;
    ss<<x;
    string res;
    ss>>res;
    return res;
    /*   下面这种写法存在bug,以后不要采用这样的方式了
    if(x==0)
        return "0";
    string res="";
    while(x>0) {
        char temp = (x%10) + '0';
        res = temp + res;
        x/=10;
    }
    return res;
    */
}
void DFS(int root,string path) {
    if(root*2>N) {
        if(root<=N && resPath.find(path)==resPath.end()) {   //root<=N的条件限制不要忘记
            cout<<path<<endl;
            resPath.insert(path);
        }
    } else {
        DFS(root*2+1,path+" "+Tran(tree[root*2+1]));
        DFS(root*2,path+" "+Tran(tree[root*2]));
    }
}
int main() {
    cin>>N;
    int flag=0;  // 0表示未出现父子结点值不相同的情况,1表示小顶堆,2表示大顶堆,-1表示不是堆
    for(int i=1; i<=N; i++) {
        cin>>tree[i];
        if(flag==-1)
            continue;
        if(i>1) {
            if(tree[i/2] < tree[i]) {
                if(flag==0)
                    flag=1;
                else if(flag==2)
                    flag=-1;
            } else if(tree[i/2] > tree[i]) {
                if(flag==0)
                    flag=2;
                else if(flag==1)
                    flag=-1;
            }
        }
    }
    DFS(1,Tran(tree[1]));
    if(flag==1)
        cout<<"Min Heap"<<endl;
    else if(flag==2)
        cout<<"Max Heap"<<endl;
    else if(flag==-1)
        cout<<"Not Heap"<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值