PAT刷题(七)

随时更新,遇到好题就更新!
这次我主要练习C和C++(需要使用STL就用C++),练习网站有两个:
codeupPTA

PAT A 1013 Battle Over Cities (查找连通块数量)

这题的本质是找到删除一个点及其连通边后,原图中还存在几个连通块,可以通过并查集来做,也可以通过DFS来做

并查集

代码如下:

#include <iostream>
#include <stdio.h>
#include <set>
using namespace std;
const int maxn=10000000;
struct Edge{
    int c1;
    int c2;
}edge[maxn];
int father[1010];
set<int>count;
int N,M,K;
void init(){
    for(int i=1;i<=N;i++){
        father[i]=i;
    }
}
int findFather(int x){
    int a=x;
    while(father[x]!=x){
        x=father[x];
    }
    while(a!=father[a]){
        int z=a;
        a=father[a];
        father[a]=x;
    }
    return x;
}
void unionNode(int x,int y){
    int faA=findFather(x);
    int faB=findFather(y);
    if(faA!=faB){
        father[faA]=faB;
    }
}
int main(){
    scanf("%d%d%d",&N,&M,&K);
    for(int i=0;i<M;i++){
        scanf("%d%d",&edge[i].c1,&edge[i].c2);
    }
    for(int i=0;i<K;i++){
        int eCity;
        scanf("%d",&eCity);
        init();
        for(int j=0;j<M;j++){
            if(edge[j].c1!=eCity&&edge[j].c2!=eCity){
                unionNode(edge[j].c1,edge[j].c2);
            }
        }
        for(int j=1;j<=N;j++){
            count.insert(findFather(j));
        }
        printf("%d\n",count.size()-2);
        count.clear();
    }
}

DFS

代码如下:

#include <iostream>
#include <stdio.h>
using namespace std;
const int maxn=1010;
int N,M,K;
int graph[maxn][maxn];
bool visit[maxn];
void dfs(int now,int eCity){
    visit[now]=true;
    for(int h=1;h<=N;h++){
        if(graph[now][h]&&!visit[h]&&h!=eCity){
            dfs(h,eCity);
        }
    }
}
int main(){
    scanf("%d%d%d",&N,&M,&K);
    fill(graph[0],graph[0]+maxn*maxn,0);
    for(int i=0;i<M;i++){
        int city1,city2;
        scanf("%d%d",&city1,&city2);
        graph[city1][city2]=1;
        graph[city2][city1]=1;
    }
    for(int i=0;i<K;i++){
        int eCity;
        int ans=0;
        scanf("%d",&eCity);
        fill(visit,visit+maxn,false);
        for(int j=1;j<=N;j++){
            if(!visit[j]&&j!=eCity){
                dfs(j,eCity);
                ans++;
            }
        }
        printf("%d\n",ans-1);
    }
}

DFS基本模板

leetcode 695.岛屿的最大面积为例

class Solution {
public:
    int dir[4][2]={{-1,0},{1,0},{0,1},{0,-1}};
    int m,n;
    int dfs(int i,int j,vector<vector<int>>& visit,vector<vector<int>>& grid){
        if(i<0||i>=m||j<0||j>=n||visit[i][j]||!grid[i][j])return 0;//判断是否符合规定
        visit[i][j]=1;
        int ans=1;
        for(int h=0;h<4;h++){
            int newI=i+dir[h][0];
            int newJ=j+dir[h][1];
            ans+=dfs(newI,newJ,visit,grid);
        }
        return ans;
    }
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        m=grid.size();
        n=grid[0].size();
        vector<vector<int>>visit(m,vector<int>(n));
        int ans=0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(!visit[i][j]&&grid[i][j]==1){
                    ans=max(ans,dfs(i,j,visit,grid));
                }
            }
        }
        return ans;
    }
};

获取链表的中间节点

快慢指针法,适用于任何情况,代码如下:

ListNode* middleNode(ListNode* head) {
    ListNode *quick=head;
    ListNode *soft=head;
    while(quick!=NULL&&quick->next!=NULL){
        quick=quick->next;
        quick=quick->next;
        soft=soft->next;
    }
    return soft;
}

PAT 1134 Vertex Cover

这道题的话如果使用的是数组来存储图的话会造成内存超限,采用vector后就可以了,代码如下:

#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
const int maxn=10010;
vector<int>graph[maxn];
bool visit[maxn];
int main(){

    int N,M;
    scanf("%d%d",&N,&M);
    for(int i=0;i<M;i++){
        int n1,n2;
        scanf("%d%d",&n1,&n2);
        graph[n1].push_back(n2);
        graph[n2].push_back(n1);
    }
    int K;
    scanf("%d",&K);
    for(int i=0;i<K;i++){
        int NV;
        scanf("%d",&NV);
        fill(visit,visit+maxn,false);
        int count=0;
        for(int j=0;j<NV;j++){
            int v;
            scanf("%d",&v);
            visit[v]=true;
            for(int h=0;h<graph[v].size();h++){
                if(visit[graph[v][h]]==false){
                    count++;
                }
            }
        }
        if(count==M){
            printf("Yes\n");
        }else{
            printf("No\n");
        }
    }
}

这道题的思路就是遍历给定的顶点集中的顶点,判断与这些顶点相连的没有重复(位于集合中的两顶点之间的边)的边的数量是否为M即可。

PAT A 1154 Vertex Coloring

代码如下:

#include <iostream>
#include <stdio.h>
#include <vector>
#include <set>
using namespace std;
const int maxn=10010;
struct Node{
    int color;
    vector<int>point;
}graph[maxn];
bool visit[maxn];
int main(){
    int N,M;
    scanf("%d%d",&N,&M);
    for(int i=0;i<M;i++){
        int n1,n2;
        scanf("%d%d",&n1,&n2);
        graph[n1].point.push_back(n2);
        graph[n2].point.push_back(n1);
    }
    int K;
    scanf("%d",&K);
    for(int i=0;i<K;i++){
        for(int j=0;j<N;j++){
            scanf("%d",&graph[j].color);
        }
        //judge
        fill(visit,visit+N,false);
        set<int>color;
        bool judge=false;
        for(int j=0;j<N;j++){
            int len=graph[j].point.size();
            color.insert(graph[j].color);
            for(int h=0;h<len;h++){
                int next=graph[j].point[h];
                if(visit[next]==false){
                    if(graph[j].color==graph[next].color){
                        judge=true;
                        break;
                    }
                }
            }
            if(judge)break;
        }
        if(judge){
            printf("No\n");
        }else{
            printf("%d-coloring\n",color.size());
        }
    }
}

和上一道题差不多,这里学会另一种用vector表示图的方法即可。

树-统计每一层的节点的数量

之前有好多题需要统计每一层节点的数量,现在总结一下,主要的思路就是维护一个当前层次中还在队列中的节点的数量,然后每pop一次,代表该层在队列中的节点的数量就会减一,减到0时代表此时该层节点已经全部pop完毕,此时队列中的节点全部都是下一层的,因此可以继续更新该值,以此类推不断维护,在这里以PAT A 1094 The Largest Generation这题为例,该题还统计了每层的高度

PAT A 1094 The Largest Generation

代码如下:

#include <iostream>
#include <stdio.h>
#include <vector>
#include <queue>
using namespace std;
const int maxn=110;
struct Node{
    vector<int>child;
}node[maxn];
int main(){
    int N,M;
    scanf("%d%d",&N,&M);
    for(int i=0;i<M;i++){
        int ID,K;
        scanf("%d%d",&ID,&K);
        for(int j=0;j<K;j++){
            int childID;
            scanf("%d",&childID);
            node[ID].child.push_back(childID);
        }
    }
    queue<int>q;
    q.push(1);
    int cont=1;
    int height=1;
    int maxcont=1;
    int maxlevel=1;
    while(!q.empty()){
        int top=q.front();
        q.pop();
        cont--;
        for(int i=0;i<node[top].child.size();i++){
            q.push(node[top].child[i]);
        }
        if(cont==0){
            cont=q.size();
            height++;
            if(cont>maxcont){
                maxcont=cont;
                maxlevel=height;
            }
        }
    }
    printf("%d %d",maxcont,maxlevel);
}

统计树的深度和处在最深处的叶子结点的数量

直接靠DFS就可以了,若需要统计处在最深处的叶子结点的数量的话直接在树的每一个节点上加一个深度/高度的属性即可,等dfs获取到深度后遍历出结果,或者维护一个各深度处节点数量的数组,下面以PAT A 1090 Highest Price in Supply Chain为例

PAT A 1090 Highest Price in Supply Chain

代码如下:

#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
const int maxn=100010;
struct Node{
    vector<int>child;
    int height;
}node[maxn];
int heightArray[maxn];
int dfs(int nodeNum){
    if(node[nodeNum].child.size()==0)return 0;
    int num=0;
    for(int i=0;i<node[nodeNum].child.size();i++){
        int child=node[nodeNum].child[i];
        node[child].height=node[nodeNum].height+1;
        heightArray[node[child].height]++;
        num=max(num,dfs(child));
    }
    return num+1;
}
int main(){
    int N;
    double P,r;
    scanf("%d%lf%lf",&N,&P,&r);
    int root;
    fill(heightArray,heightArray+N,0);
    for(int i=0;i<N;i++){
        int temp;
        scanf("%d",&temp);
        if(temp==-1){
            root=i;
            node[root].height=0;
        }else{
            node[temp].child.push_back(i);
        }
    }
    heightArray[0]=1;
    int height=dfs(root);
    for(int i=0;i<height;i++){
        P*=(1+r*0.01);
    }
    printf("%.2f %d",P,heightArray[height]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值