PAT甲级 图论

考点

1、 Dijkstra的各种变形
2、连通性,判断连通块数量
3、层次遍历
4、边性质的遍历

基础预备——Dijkstra I

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 510;

int n, m;
int g[N][N];
int dist[N];
bool st[N];

int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    for (int i = 0; i < n - 1; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        for (int j = 1; j <= n; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);

        st[t] = true;
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

int main()
{
    scanf("%d%d", &n, &m);

    memset(g, 0x3f, sizeof g);
    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);

        g[a][b] = min(g[a][b], c);
    }

    printf("%d\n", dijkstra());

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/308477/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

基础知识——Dijkstra II

#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

typedef pair<int, int> PII;

const int N = 1e6 + 10;

int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 1});

    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.second, distance = t.first;

        if (st[ver]) continue;
        st[ver] = true;

        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[ver] + w[i])
            {
                dist[j] = dist[ver] + w[i];
                heap.push({dist[j], j});
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

int main()
{
    scanf("%d%d", &n, &m);

    memset(h, -1, sizeof h);
    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
    }

    cout << dijkstra() << endl;

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/308478/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

紧急情况

甲级1003

题目

作为城市的紧急救援团队负责人,你将获得一张你所在国家的特殊地图。

该地图显示了一些通过道路连接的分散城市,道路是双向的。

地图上标出了每个城市的救援队数量以及每对城市之间的每条道路的长度。

当其他城市发出紧急求援信息时,你的工作是尽快带领你的士兵前往该地点,同时,在途中尽可能多地调动救援帮手。

输入格式
第一行包含四个整数 N,表示城市数量(城市编号从 0 到 N−1),M 表示道路数量,C1 表示你当前所在的城市编号,C2 表示发出紧急求援信息的城市编号。

第二行包含 N 个整数,其中第 i 个整数表示城市 i 的救援队数量。

接下来 M 行,每行包含三个整数 c1,c2,Li,表示城市 c1 和城市 c2 之间存在一条道路相连,道路长度为 Li。

数据保证 C1 和 C2 之间至少存在一条路径相连。

输出格式
共一行,两个整数,第一个整数表示 C1 和 C2 之间最短路的数量,第二个整数表示走最短路的情况下,能聚集到的救援队最大数量。

数据范围
2≤N≤500,
1≤M≤600,
1≤Li≤200,
每个城市包含的救援人员数量不超过 200。

输入样例:
5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1
输出样例:
2 4

思想

可以发现本道题是一个Dijkstra的变形,
在求得最短路径的情况下,求解救援人员最多的那种情况。——sum
并统计最短路的数量——cnt[j]

涉及最短路径的保存

        for(j = 0; j < n; j ++){
            if(dist[j] > dist[t] + d[t][j]){
                dist[j] = dist[t] + d[t][j];
                cnt[j] = cnt[t];
                sum[j] = sum[t] + w[j];
            }
            else if(dist[j] == dist[t] + d[t][j]){
                cnt[j] += cnt[t];
                sum[j] = max(sum[j], sum[t] + w[j]);
            }
        }

我的代码

#include <iostream>
#include <cstring>
using namespace std;
const int N = 510;
int n, m, c1, c2;
int cnt[N], sum[N], w[N], d[N][N], dist[N];
bool st[N];
void dijkstra(){
    int i, j;
    memset(dist, 0x3f, sizeof dist);
    dist[c1] = 0, cnt[c1] = 1, sum[c1] = w[c1];
    for(i = 0; i < n; i ++){
        int t = -1;
        for(j = 0; j < n; j ++){
            if(!st[j] && (t == -1 || dist[j] < dist[t]))
                t = j;
        }
        st[t] = true;
        for(j = 0; j < n; j ++){
            if(dist[j] > dist[t] + d[t][j]){
                dist[j] = dist[t] + d[t][j];
                cnt[j] = cnt[t];
                sum[j] = sum[t] + w[j];
            }
            else if(dist[j] == dist[t] + d[t][j]){
                cnt[j] += cnt[t];
                sum[j] = max(sum[j], sum[t] + w[j]);
            }
        }
    }
}


int main(){
    cin>>n>>m>>c1>>c2;
    for (int i = 0; i < n; i ++ ) cin >> w[i];

    memset(d, 0x3f, sizeof d);
    while (m -- )
    {
        int a, b, c;
        cin >> a >> b >> c;
        d[a][b] = d[b][a] = min(d[a][b], c);
    }
    
    dijkstra();
    cout<<cnt[c2]<<" "<<sum[c2]<<endl;
}

y总的代码

#include <cstring>
#include <iostream>

using namespace std;

const int N = 510;

int n, m, S, T;
int w[N], d[N][N];
int dist[N], cnt[N], sum[N];
bool st[N];

void dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[S] = 0, cnt[S] = 1, sum[S] = w[S];

    for (int i = 0; i < n; i ++ )
    {
        int t = -1;
        for (int j = 0; j < n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
        st[t] = true;

        for (int j = 0; j < n; j ++ )
            if (dist[j] > dist[t] + d[t][j])
            {
                dist[j] = dist[t] + d[t][j];
                cnt[j] = cnt[t];
                sum[j] = sum[t] + w[j];
            }
            else if (dist[j] == dist[t] + d[t][j])
            {
                cnt[j] += cnt[t];
                sum[j] = max(sum[j], sum[t] + w[j]);
            }
    }
}

int main()
{
    cin >> n >> m >> S >> T;

    for (int i = 0; i < n; i ++ ) cin >> w[i];

    memset(d, 0x3f, sizeof d);
    while (m -- )
    {
        int a, b, c;
        cin >> a >> b >> c;
        d[a][b] = d[b][a] = min(d[a][b], c);
    }

    dijkstra();

    cout << cnt[T] << ' ' << sum[T] << endl;

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/294255/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

旅行计划

甲级1030

题目

给定一张地图,包含 N 个城市,M 条高速公路。

城市之间都能相互连通。

每条高速公路的长度和走该条公路的花费都是已知的,高速公路都是双向的。

现在要从地图中的某个城市前往另一个城市。

请你确定最短路径,当最短路径不唯一时,请你选取花费最小的路径(保证唯一)。

输入格式
第一行包含四个整数 N,M,S,D,分别表示城市数量,公路数量,起点城市编号,终点城市编号。

城市编号从 0 到 N−1。

接下来 M 行,每行包含四个整数 a,b,c,d,表示城市 a 和城市 b 之间存在一条公路,长度为 c,花费为 d。

输出格式
共一行,首先输出从起点城市到终点城市的最短路径(花费最少的)经过的所有城市,然后输出最短路径的距离以及最小的花费。

数据范围
1≤N≤500,
1≤M≤600,
1≤c,d≤500
输入样例:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
输出样例:
0 2 3 3 40

思想

这道题仍然是Dijkstra的变形
要求在求最短路径的所有方案中的最少花费,
并记录路径

在这里插入代码片

我的代码

        for (int j = 0; j < n; j ++ )
            if (dist[j] > dist[t] + d[t][j])
            {
                dist[j] = dist[t] + d[t][j];
                cost[j] = cost[t] + c[t][j];
                pre[j] = t;
            }
            else if (dist[j] == dist[t] + d[t][j] && cost[j] > cost[t] + c[t][j])
            {
                cost[j] = cost[t] + c[t][j];
                pre[j] = t;
            }
    }

y总的代码

#include <iostream>
#include <cstring>
#include <vector>

using namespace std;

const int N = 510;

int n, m, S, T;
int d[N][N], c[N][N];
int dist[N], cost[N], pre[N];
bool st[N];

void dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    memset(cost, 0x3f, sizeof cost);

    dist[S] = 0, cost[S] = 0;
    for (int i = 0; i < n; i ++ )
    {
        int t = -1;
        for (int j = 0; j < n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
        st[t] = true;

        for (int j = 0; j < n; j ++ )
            if (dist[j] > dist[t] + d[t][j])
            {
                dist[j] = dist[t] + d[t][j];
                cost[j] = cost[t] + c[t][j];
                pre[j] = t;
            }
            else if (dist[j] == dist[t] + d[t][j] && cost[j] > cost[t] + c[t][j])
            {
                cost[j] = cost[t] + c[t][j];
                pre[j] = t;
            }
    }
}

int main()
{
    cin >> n >> m >> S >> T;
    memset(d, 0x3f, sizeof d);
    memset(c, 0x3f, sizeof c);

    while (m -- )
    {
        int a, b, x, y;
        cin >> a >> b >> x >> y;
        if (x < d[a][b])
        {
            d[a][b] = d[b][a] = x;
            c[a][b] = c[b][a] = y;
        }
        else if (x == d[a][b] && y < c[a][b])
        {
            c[a][b] = c[b][a] = y;
        }
    }

    dijkstra();

    vector<int> path;
    for (int i = T; i != S; i = pre[i]) path.push_back(i);

    cout << S;
    for (int i = path.size() - 1; i >= 0; i -- ) cout << ' ' << path[i];
    cout << ' ' << dist[T] << ' ' << cost[T] << endl;

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/294264/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

团伙头目

题目

警察找到团伙头目的一种方法是检查人们的通话。

如果 A 和 B 之间有通话,我们就说 A 和 B 是相关的。并且关联具有传递性,即如果 A 与 B 关联,B 与 C 关联,那么 A 与 C 也是关联的。

关联权重定义为两人之间所有通话的总时间长度。

一个“帮派”是一个由至少3个相互关联的人组成的群体,并且其总关联权重大于给定的阈值 K。

在每个帮派中,总权重最大的就是头目,数据保证每个帮派中总权重最大的人是唯一的。

你需要确定各个帮派以及帮派头目。

输入格式
第一行包含两个整数 N 和 K,表示通话数量以及权重阈值。

接下来 N 行,每行采用如下形式描述通话:

Name1 Name2 Time
Name1 和 Name2 是通话的两人的名字,Time 是通话时间。

名字的长度固定为 3,且只包含大写字母。

时间是一个不超过 1000 的正整数。

输出格式
第一行输出帮派数量。

接下来每行输出一个帮派的信息,包括帮派头目名字以及帮派人数。

帮派信息按头目名字字典序输出。

数据范围
1≤N,K≤1000
输入样例1:
8 59
AAA BBB 10
BBB AAA 20
AAA CCC 40
DDD EEE 5
EEE DDD 70
FFF GGG 30
GGG HHH 20
HHH FFF 10
输出样例1:
2
AAA 3
GGG 3
输入样例2:
8 70
AAA BBB 10
BBB AAA 20
AAA CCC 40
DDD EEE 5
EEE DDD 70
FFF GGG 30
GGG HHH 20
HHH FFF 10
输出样例2:
0

思想

(1)求连通块,并且求连通块的总权值

int dfs(string u, vector<string> &node){//要返回总权值和节点,不能只返回节点数,因为要判断节点中权值最大的
    st[u] = true;
    node.push_back(u);
    int sum = 0;
    for(auto edge : g[u]){
        sum += edge.second;
        string cur = edge.first;
        if(!st[cur]) sum += dfs(cur, node);//只有没有遍历过的才加
    }
    return sum;
}

我的代码

//题目考点1:判断联通块——深搜or并查集
//题目考点2:找到联通块中权值最大的——需要记录联通块中有哪些节点,然后遍历找最大
//题目考点3: 存储图——unordered_map;记录权值——total
//题目考点4:输出需要排序
#include<iostream>
#include <unordered_map>
#include <algorithm>
#include <vector>
using namespace std;
unordered_map<string, vector<pair<string, int>>> g;
unordered_map<string, int> total;//根据题目,我需要一个来存储不同点的总权值
unordered_map<string, bool> st;
int dfs(string u, vector<string> &node){//要返回总权值和节点,不能只返回节点数,因为要判断节点中权值最大的
    st[u] = true;
    node.push_back(u);
    int sum = 0;
    for(auto edge : g[u]){
        sum += edge.second;
        string cur = edge.first;
        if(!st[cur]) sum += dfs(cur, node);//只有没有遍历过的才加
    }
    return sum;
}
int main(){
    int n, k, i;
    cin>>n>>k;
    for(i = 0; i < n; i ++){
        string a, b;
        int w;
        cin>>a>>b>>w;
        g[a].push_back({b, w});
        g[b].push_back({a, w});
        total[a] += w;
        total[b] += w;
    }
    vector<pair<string, int>> res;
    for(auto item : total){
        string ver = item.first;
        vector<string> node;
        int sum = dfs(ver, node) / 2;
        if(node.size() > 2 && sum > k){
            string boss;
            int tot = -1;
            for(auto no : node){
                if(total[no] > tot){
                    boss = no;
                    tot = total[no];
                }
            }
            res.push_back({boss, node.size()});
        }
    }
    sort(res.begin(), res.end());
    cout<<res.size()<<endl;
    for(auto re : res){
        cout<<re.first<<" "<<re.second<<endl;
    }
    
}

y总的代码

#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>

using namespace std;

int n, k;
unordered_map<string, vector<pair<string, int>>> g;
unordered_map<string, int> total;
unordered_map<string, bool> st;

int dfs(string ver, vector<string> &nodes)
{
    st[ver] = true;
    nodes.push_back(ver);

    int sum = 0;
    for (auto edge : g[ver])
    {
        sum += edge.second;
        string cur = edge.first;
        if (!st[cur]) sum += dfs(cur, nodes);
    }

    return sum;
}

int main()
{
    cin >> n >> k;
    while (n -- )
    {
        string a, b;
        int t;
        cin >> a >> b >> t;
        g[a].push_back({b, t});
        g[b].push_back({a, t});
        total[a] += t;
        total[b] += t;
    }

    vector<pair<string, int>> res;
    for (auto item : total)
    {
        string ver = item.first;
        vector<string> nodes;
        int sum = dfs(ver, nodes) / 2;

        if (nodes.size() > 2 && sum > k)
        {
            string boss = nodes[0];
            for (string node : nodes)
                if (total[boss] < total[node])
                    boss = node;
            res.push_back({boss, (int)nodes.size()});
        }
    }

    sort(res.begin(), res.end());

    cout << res.size() << endl;
    for (auto item : res) cout << item.first << ' ' << item.second << endl;

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/294285/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

条条大路通罗马

甲级1087

题目

从我们的城市到达罗马有许多不同的旅游路线。

请你在成本最低的旅游路线之中,找到使得游客幸福感最强的路线。

输入格式
第一行包含两个整数 N 和 K,分别表示总城市数量,城市之间道路数量,还包含一个城市名字,表示初始城市。

接下来 N−1 行,每行包含一个城市名和一个整数,表示到达该城市(初始城市除外)可以获得的幸福感。

接下来 K 行,每行包含一个道路的信息,格式为 City1 City2 Cost,表示两个城市之间的道路行走的花费,道路是双向的。

城市都是由三个大写字母构成的字符串。

我们的目的地始终都是罗马 ROM。

输出格式
我们应该找到成本最低的路线。

如果这样的路线不是唯一的,那么选取使人们获得最大幸福感的路线。

如果这样的路线仍然不是唯一的,那么我们选择平均幸福感最大的路线,数据保证这种路线唯一。

平均幸福感 = 总幸福感 / 经过的城市数量(初始城市不算)

第一行输出四个整数,最小成本的路线数量,最小成本,幸福感,平均幸福感(只取整数部分)。

第二行,按照 City1->City2->…->ROM 的格式输出路线。

数据范围
2≤N≤200,
1≤K≤250,
每个城市的幸福感范围在 [0,100],
每条路线最大成本不超过 1000
输入样例:
6 7 HZH
ROM 100
PKN 40
GDN 55
PRS 95
BLN 80
ROM GDN 1
BLN ROM 1
HZH PKN 1
PRS ROM 2
BLN HZH 2
PKN GDN 1
HZH PRS 1
输出样例:
3 3 195 97
HZH->PRS->ROM

思想

要记录最小成本路径数量、最大幸福感、平均幸福感——城市数量

for (int j = 1; j <= n; j ++ )
            if (dist[j] > dist[t] + d[t][j])
            {
                dist[j] = dist[t] + d[t][j];
                cnt[j] = cnt[t];
                cost[j] = cost[t] + w[j];
                sum[j] = sum[t] + 1;
                pre[j] = t;
            }
            else if (dist[j] == dist[t] + d[t][j])
            {
                cnt[j] += cnt[t];
                if (cost[j] < cost[t] + w[j])
                {
                    cost[j] = cost[t] + w[j];
                    sum[j] = sum[t] + 1;
                    pre[j] = t;
                }
                else if (cost[j] == cost[t] + w[j])
                {
                    if (sum[j] > sum[t] + 1)
                    {
                        sum[j] = sum[t] + 1;
                        pre[j] = t;
                    }
                }
            }

我的代码

//将城市字符串转换成数字,方便算法书写!!!

#include <iostream>
#include <cstring>
#include <unordered_map>
#include <vector>

using namespace std;

const int N = 210;

int n, m;
int w[N];
int d[N][N];
int dist[N], cnt[N], cost[N], sum[N], pre[N];
// 最短距离,最短路数量,最大点权,最小点数, 最短路径的前一个点
bool st[N];

string city[N];
unordered_map<string, int> mp;

void dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0, cnt[1] = 1;

    for (int i = 0; i < n; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
        st[t] = true;

        for (int j = 1; j <= n; j ++ )
            if (dist[j] > dist[t] + d[t][j])
            {
                dist[j] = dist[t] + d[t][j];
                cnt[j] = cnt[t];
                cost[j] = cost[t] + w[j];
                sum[j] = sum[t] + 1;
                pre[j] = t;
            }
            else if (dist[j] == dist[t] + d[t][j])
            {
                cnt[j] += cnt[t];
                if (cost[j] < cost[t] + w[j])
                {
                    cost[j] = cost[t] + w[j];
                    sum[j] = sum[t] + 1;
                    pre[j] = t;
                }
                else if (cost[j] == cost[t] + w[j])
                {
                    if (sum[j] > sum[t] + 1)
                    {
                        sum[j] = sum[t] + 1;
                        pre[j] = t;
                    }
                }
            }
    }
}

int main()
{
    cin >> n >> m >> city[1];
    mp[city[1]] = 1;

    for (int i = 2; i <= n; i ++ )
    {
        cin >> city[i] >> w[i];
        mp[city[i]] = i;
    }

    memset(d, 0x3f, sizeof d);
    while (m -- )
    {
        string x, y;
        int c;
        cin >> x >> y >> c;
        int a = mp[x], b = mp[y];
        d[a][b] = d[b][a] = min(d[a][b], c);
    }

    dijkstra();

    int T = mp["ROM"];
    cout << cnt[T] << ' ' << dist[T] << ' ' << cost[T] << ' ' << cost[T] / sum[T] << endl;

    vector<int> path;
    for (int i = T; i != 1; i = pre[i]) path.push_back(i);

    cout << city[1];
    for (int i = path.size() - 1; i >= 0; i -- )
        cout << "->" << city[path[i]];
    cout << endl;

    return 0;
}

y总的代码

#include <iostream>
#include <cstring>
#include <unordered_map>
#include <vector>

using namespace std;

const int N = 210;

int n, m;
int w[N];
int d[N][N];
int dist[N], cnt[N], cost[N], sum[N], pre[N];
// 最短距离,最短路数量,最大点权,最小点数, 最短路径的前一个点
bool st[N];

string city[N];
unordered_map<string, int> mp;

void dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0, cnt[1] = 1;

    for (int i = 0; i < n; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
        st[t] = true;

        for (int j = 1; j <= n; j ++ )
            if (dist[j] > dist[t] + d[t][j])
            {
                dist[j] = dist[t] + d[t][j];
                cnt[j] = cnt[t];
                cost[j] = cost[t] + w[j];
                sum[j] = sum[t] + 1;
                pre[j] = t;
            }
            else if (dist[j] == dist[t] + d[t][j])
            {
                cnt[j] += cnt[t];
                if (cost[j] < cost[t] + w[j])
                {
                    cost[j] = cost[t] + w[j];
                    sum[j] = sum[t] + 1;
                    pre[j] = t;
                }
                else if (cost[j] == cost[t] + w[j])
                {
                    if (sum[j] > sum[t] + 1)
                    {
                        sum[j] = sum[t] + 1;
                        pre[j] = t;
                    }
                }
            }
    }
}

int main()
{
    cin >> n >> m >> city[1];
    mp[city[1]] = 1;

    for (int i = 2; i <= n; i ++ )
    {
        cin >> city[i] >> w[i];
        mp[city[i]] = i;
    }

    memset(d, 0x3f, sizeof d);
    while (m -- )
    {
        string x, y;
        int c;
        cin >> x >> y >> c;
        int a = mp[x], b = mp[y];
        d[a][b] = d[b][a] = min(d[a][b], c);
    }

    dijkstra();

    int T = mp["ROM"];
    cout << cnt[T] << ' ' << dist[T] << ' ' << cost[T] << ' ' << cost[T] / sum[T] << endl;

    vector<int> path;
    for (int i = T; i != 1; i = pre[i]) path.push_back(i);

    cout << city[1];
    for (int i = path.size() - 1; i >= 0; i -- )
        cout << "->" << city[path[i]];
    cout << endl;

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/294300/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

在线地图

甲级1111

题目

输入我们的当前位置和目的地,在线地图就可以推荐一些行进路线。

现在你的工作是向用户推荐两条路线:一条是最短路线,另一条是最快路线。

保证任何询问的两地之间都存在至少一条路线。

输入格式
第一行包含两个整数 N 和 M,表示地图中共有 N 个路口(编号 0∼N−1)和 M 个街道。

接下来 M 行,每行描述一条街道,格式如下:

V1 V2 one-way length time
V1 和 V2 是两个路口的编号,表示这两个路口之间存在一条街道,one-way 如果为 1 则表示这条街道是单行道,只能从 V1 前往 V2。如果为 0 表示是双行道,可随意通行。 length 是街道的长度,time 是通过这条街道花费的时间。

最后一行,将给出起点和终点路口的编号。

输出格式
第一行输出路程最短的路线,并输出最短路程 D,格式如下:

Distance = D: source -> v1 -> … -> destination
第二行输出用时最快的路线,并输出最短用时 T,格式如下:

Time = T: source -> w1 -> … -> destination
如果最短路线不唯一,则输出用时最短的那条路线(保证唯一)。

如果最快路线不唯一,则输出经过路口最少的那条路线(保证唯一)。

如果最短路线和最快路线经过的路口序列完全相同,则以如下格式将两个信息输出在一行:

Distance = D; Time = T: source -> u1 -> … -> destination
数据范围
2≤N≤500,
1≤M≤N(N−1)2,
每条道路的长度和用时都不超过 1000。
任意两个路口之间可能出现重复街道。(这一点和官网不同)

输入样例1:
10 15
0 1 0 1 1
8 0 0 1 1
4 8 1 1 1
3 4 0 3 2
3 9 1 4 1
0 6 0 1 1
7 5 1 2 1
8 5 1 2 1
2 3 0 2 2
2 1 1 1 1
1 3 0 3 1
1 4 0 1 1
9 7 1 3 1
5 1 0 5 2
6 5 1 1 2
3 5
输出样例1:
Distance = 6: 3 -> 4 -> 8 -> 5
Time = 3: 3 -> 1 -> 5
输入样例2:
7 9
0 4 1 1 1
1 6 1 1 3
2 6 1 1 1
2 5 1 2 2
3 0 0 1 1
3 1 1 1 3
3 2 1 1 2
4 5 0 2 2
6 5 1 1 2
3 5
输出样例2:
Distance = 3; Time = 4: 3 -> 2 -> 5
难度:中等
时/空限制:0.4s / 64MB
总通过数:408
总尝试数:1026
来源:PAT甲级真题1111

思想

使用两次Dijkstra
第一次求最短路径,且最快的那一条
要记录路径和用时
第二次求最快路径,且经过路口数最少的
要记录用时和路口数

自己的代码

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int N = 510;
int dist[N], tt[N], d[N][N], ti[N][N], sum[N];
int n, m, S,T;
int dijkstra(int type, vector<int> &path){
    memset(dist, 0x3f, sizeof dist);
    memset(tt, 0x3f, sizeof tt);
    dist[S] = 0, tt[S] = 0;
    int i, j, pre[N];
    bool st[N];
    for(i = 0; i < n; i ++){
        int t = -1;
        for(j = 0; j < n; j ++){
            if(!st[j] && (t == -1 || dist[j] < dist[t])){
                t = j;
            }
        }
        st[t] = true;
        if(type == 1){
            for(j = 0; j < n; j ++){
                if(dist[j] > dist[t] + d[t][j]){
                    dist[j] = dist[t] + d[t][j];
                    tt[j] = tt[t] + ti[t][j];
                    pre[j] = t;
                }
                else if(dist[j] == dist[t] + d[t][j] && tt[j] > tt[t] + ti[t][j]){
                    tt[j] = tt[t] + ti[t][j];
                    pre[j] = t;
                }
            }
        }
        else if(type == 2){
            for(j = 0; j < n; j ++){
                if(dist[j] > dist[t] + ti[t][j]){
                    dist[j] = dist[t] + ti[t][j];
                    sum[j] = sum[t] + 1;
                    pre[j] = t;
                }
                else if(dist[j] == dist[t] + d[t][j] && sum[j] > sum[t] + 1){
                    sum[j] = sum[t] + 1;
                    pre[j] = t;
                }
            }
        }
    }
    path.push_back(S);
    vector<int> temp;
    for(i = T; i != S; i = pre[i]) temp.push_back(i);
    for(i = temp.size() - 1; i >= 0; i --) path.push_back(temp[i]);
    
    return dist[T];
}
int main(){
    int i;
    cin>>n>>m;
    memset(d, 0x3f, sizeof d);
    memset(ti, 0x3f, sizeof ti);
    for(i = 0; i < m; i ++){
        int a, b, is_oneway, length, tim;
        cin>>a>>b>>is_oneway>>length>>tim;
        if(is_oneway){
            d[a][b] = min(d[a][b], length);
            ti[a][b] = min(ti[a][b], tim);
        }
        else{
            d[a][b] = d[b][a] = min(d[a][b], length);
            ti[a][b] = ti[b][a] = min(ti[a][b], tim);            
        }
    }
    cin>>S>>T;
    vector<int> path1, path2;
    int res1 = dijkstra(1, path1);
    int res2 = dijkstra(2, path2);
    if(path1 == path2){
        cout<<"Distance = "<<res1<<"; Time = "<<res2<<": "<<path1[0];
        for(i = 1; i < path1.size(); i ++)
            cout<<" -> "<<path1[i];
        cout<<endl;
    }
    else{
        cout<<"Distance = "<<res1<<": "<<path1[0];
        for(i = 1; i < path1.size(); i ++)
            cout<<" -> "<<path1[i];
        cout<<endl;
        cout<<"Time = "<<res2<<": "<<path2[0];
        for(i = 1; i < path2.size(); i ++)
            cout<<" -> "<<path2[i];
        cout<<endl;
    }
    
}

y总的代码

#include <cstring>
#include <iostream>
#include <vector>

using namespace std;

const int N = 510, M = N * N;

int n, m, S, T;
int h[N], e[M], w1[M], w2[M], ne[M], idx;
int dist1[N], dist2[N], pre[N];
bool st[N];

void add(int a, int b, int c, int d)
{
    e[idx] = b, w1[idx] = c, w2[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
}

pair<int, string> dijkstra(int w1[], int w2[], int type)
{
    memset(dist1, 0x3f, sizeof dist1);
    memset(dist2, 0x3f, sizeof dist2);
    memset(st, 0, sizeof st);
    dist1[S] = dist2[S] = 0;

    for (int i = 0; i < n; i ++ )
    {
        int t = -1;
        for (int j = 0; j < n; j ++ )
            if (!st[j] && (t == -1 || dist1[t] > dist1[j]))
                t = j;
        st[t] = true;
        for (int u = h[t]; ~u; u = ne[u])
        {
            int j = e[u];
            int cost;
            if (type == 0) cost = w2[u];
            else cost = 1;

            if (dist1[j] > dist1[t] + w1[u])
            {
                dist1[j] = dist1[t] + w1[u];
                dist2[j] = dist2[t] + cost;
                pre[j] = t;
            }
            else if (dist1[j] == dist1[t] + w1[u])
            {
                if (dist2[j] > dist2[t] + cost)
                {
                    dist2[j] = dist2[t] + cost;
                    pre[j] = t;
                }
            }
        }
    }

    vector<int> path;
    for (int i = T; i != S; i = pre[i]) path.push_back(i);

    pair<int, string> res;
    res.first = dist1[T];
    res.second = to_string(S);
    for (int i = path.size() - 1; i >= 0; i -- )
        res.second += " -> " + to_string(path[i]);
    return res;
}

int main()
{
    cin >> n >> m;

    memset(h, -1, sizeof h);
    while (m -- )
    {
        int a, b, t, c, d;
        cin >> a >> b >> t >> c >> d;
        add(a, b, c, d);
        if (!t) add(b, a, c, d);
    }

    cin >> S >> T;

    auto A = dijkstra(w1, w2, 0);
    auto B = dijkstra(w2, w1, 1);

    if (A.second != B.second)
    {
        printf("Distance = %d: %s\n", A.first, A.second.c_str());
        printf("Time = %d: %s\n", B.first, B.second.c_str());
    }
    else
    {
        printf("Distance = %d; Time = %d: %s\n", A.first, B.first, A.second.c_str());
    }

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/294319/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

哈密顿回路

题目

自己的代码

y总的代码

欧拉回路

甲级1126

题目

在图论中,欧拉路径是图中的一条路径,该路径满足恰好访问每个边一次。

而欧拉回路是一条在同一顶点处开始和结束的欧拉路径。

它们最早由欧拉于 1736 年解决著名的哥尼斯堡七桥问题时提出。

事实证明,如果一个连通图的所有顶点的度数都为偶数,那么这个连通图具有欧拉回路,且这个图被称为欧拉图。

如果一个连通图中有两个顶点的度数为奇数,其他顶点的度数为偶数,那么所有欧拉路径都从其中一个度数为奇数的顶点开始,并在另一个度数为奇数的顶点结束。

具有欧拉路径但不具有欧拉回路的图被称为半欧拉图。

现在,给定一个无向图,请你判断它是欧拉图、半欧拉图还是非欧拉图。

输入格式
第一行包含两个整数 N 和 M,表示无向图的点和边的数量。

接下来 M 行,每行包含两个整数 a,b,表示点 a 和 b 之间存在一条边。

所有点的编号从 1∼N。

输出格式
首先,在第一行按顺序输出点 1∼N 中每个点的度数。

第二行输出对该图的判断,Eulerian(欧拉图),Semi-Eulerian(半欧拉图),Non-Eulerian(非欧拉图)。

行尾不得有多余空格。

数据范围
1≤N≤500,
1≤M≤N(N−1)2
输入样例1:
7 12
5 7
1 2
1 3
2 3
2 4
3 4
5 2
7 6
6 3
4 5
6 4
5 6
输出样例1:
2 4 4 4 4 4 2
Eulerian
输入样例2:
6 10
1 2
1 3
2 3
2 4
3 4
5 2
6 3
4 5
6 4
5 6
输出样例2:
2 4 4 4 3 3
Semi-Eulerian
输入样例3:
5 8
1 2
2 5
5 4
4 1
1 3
3 2
3 4
5 3
输出样例3:
3 3 4 3 3
Non-Eulerian

思想

欧拉图-1、连通 2、所有点的度数为偶数
半欧拉图 1、连通2、度数2个奇数其余都为偶数

判断联通性,以及连通块中的个数

//判断联通性,以及联通块的个数
int dfs(int u)
{
    st[u] = true;

    int res = 1;
    for (int i = 1; i <= n; i ++ )
        if (!st[i] && g[u][i])
            res += dfs(i);

    return res;
}

自己的代码

/*判断联通性,以及联通块的个数
int dfs(int u)
{
    st[u] = true;

    int res = 1;
    for (int i = 1; i <= n; i ++ )
        if (!st[i] && g[u][i])
            res += dfs(i);

    return res;
}
*/





//欧拉图-1、连通 2、所有点的度数为偶数
//半欧拉图 1、连通2、2个奇数其余都为偶数

#include<iostream>
using namespace std;
const int N = 1e3 + 10;
int g[N][N], cnt[N];
bool st[N];

int n, m;
void dfs(int u){
    st[u] = true;
    for(int i = 1; i <= n; i ++){
        if(g[u][i] && !st[i])
            dfs(i);
    }
}
bool DFS(int u){
    dfs(u);
    for(int i = 1; i <= n; i ++){
        if(!st[i]) return false;
    }
    return true;
}
int main(){
    cin>>n>>m;
    for(int i = 0; i < m; i ++){
        int a, b;
        cin>>a>>b;
        g[a][b] = 1;
        g[b][a] = 1;
        cnt[a] ++;
        cnt[b] ++;
    }
    bool flag = DFS(1), is_oula, is_midoula;
    int count = 0;
    for(int i = 1; i <= n; i ++){
        if(i > 1) cout<<" ";
        printf("%d", cnt[i]);
        if(cnt[i] % 2) count++;
    }
    cout<<endl;
    if(flag && count == 0){
        cout<<"Eulerian"<<endl;
    }
    else if(flag && count == 2){
        cout<<"Semi-Eulerian"<<endl;
    }
    else{
        cout<<"Non-Eulerian"<<endl;
    }
}

y总的代码

#include <cstring>
#include <iostream>

using namespace std;

const int N = 510;

int n, m;
bool g[N][N], st[N];
int d[N];

int dfs(int u)
{
    st[u] = true;

    int res = 1;
    for (int i = 1; i <= n; i ++ )
        if (!st[i] && g[u][i])
            res += dfs(i);

    return res;
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; i ++ )
    {
        int a, b;
        cin >> a >> b;
        g[a][b] = g[b][a] = true;
        d[a] ++, d[b] ++ ;
    }

    int cnt = dfs(1);

    cout << d[1];
    for (int i = 2; i <= n; i ++ ) cout << ' ' << d[i];
    cout << endl;
    if (cnt == n)
    {
        int s = 0;
        for (int i = 1; i <= n; i ++ )
            if (d[i] % 2)
                s ++ ;

        if (s == 0) puts("Eulerian");
        else if (s == 2) puts("Semi-Eulerian");
        else puts("Non-Eulerian");
    }
    else puts("Non-Eulerian");

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/295441/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

地铁地图

题目

在大城市,地铁系统对于游客来说总是看起来如此复杂。

为了使你对此有所了解,下图显示了北京地铁的地图。

现在,请你帮助编写一个程序,给定用户的起始位置,找到到达其目的地的最快方法。

55799c23-4bdb-4e32-af7f-6d41accfdd2b.jpg

输入格式
第一行包含一个正整数 N,表示地铁线路的数量。

接下来 N 行,第 i 行以下列格式描述第 i 条线路(地铁线路都是双向的):

M S[1] S[2] … S[M]

其中 M 是站点数量,S[i] 是沿线站点的编号(站点编号是从 0000 到 9999 的 4 位数字)。

确保这些站点是按照地铁行进顺序给出的,即地铁会从 S[i] 直接开到 S[i+1]。

注意,可能会存在回路,但不存在自环(即地铁从 S 出发,直接开向 S,中途不经过任何站点)。

每个车站间隔都只属于一条唯一线路。

一些线路可能会在某些站点(中转站)彼此交叉,但是不能有任何站点作为中转站时,有超过 5 条线路在该站点交汇。

描述完地铁线路信息后,包含一行整数 K,表示询问次数。

接下来 K 行,每行描述一个询问,包含两个站点编号分别表示始发站和目的地。

下面是示例图:

932c8f1b-7dd5-489d-a774-a91c1fabba7f.jpg

保证所有站点都能到达,询问站点编号合法。

输出格式
对于每个询问,首先输出最少需要停靠的站点数量,然后以如下格式输出最佳线路:

Take Line#X1 from S1 to S2.
Take Line#X2 from S2 to S3.

其中 Xi 是线路编号,Si 是站点编号,除始发站和终点站外,只输出中转站。

如果最快线路不唯一,则输出换乘次数最少的线路,保证唯一。

数据范围
1≤N≤100,
1≤M≤100,
1≤K≤10
输入样例:
4
7 1001 3212 1003 1204 1005 1306 7797
9 9988 2333 1204 2006 2005 2004 2003 2302 2001
13 3011 3812 3013 3001 1306 3003 2333 3066 3212 3008 2302 3010 3011
4 6666 8432 4011 1306
3
3011 3013
6666 2001
2004 3001
输出样例:
2
Take Line#3 from 3011 to 3013.
10
Take Line#4 from 6666 to 1306.
Take Line#3 from 1306 to 2302.
Take Line#2 from 2302 to 2001.
6
Take Line#2 from 2004 to 1204.
Take Line#1 from 1204 to 1306.
Take Line#3 from 1306 to 3001.

思想

这道题非常经典,要完全搞明白

这道题的关键在于,将一条地铁线路中的每个点都连起来,存成对应边和对应点,这样,一条边就可以代表这一个线路
在上面的基础上,换成次数最少,就变成了了边数最少
于是问题变成了求边数最少的最短路问题

这道题需要用到堆优化,因为点数较多,不可能遍历寻找最小值
于是使用堆优化去求解
在存储边数的时候,可以顺便把距离和所属的路线一起存了

堆优化的Dijkstra

while(heap.size()){//由于点较分散,于是我们使用堆优化的方法区进行
        
        auto t = heap.top().second;
        heap.pop();
        
        //st[t] = true;
        if(t == b) break;//这个很关键,堆不一定会变空,所以如果终点确定下来了就退出循环
        if(st[t]) continue;//堆优化,没有对st的判断,所以如果st已经确定了,则直接continue
        
        st[t] = true;
        
        for(int i = h[t]; i != -1; i = ne[i]){
            int j = e[i];
            if(d[j] > d[t] + w[i]){
                d[j] = d[t] + w[i];
                heap.push({d[j], j});
                cnt[j] = cnt[t] + 1;
                pre[j] = t;
                //info[j] = "Take Line#" + to_string(line[j]) + "from "+ to_string(t) +" to "+ to_string(j) <<endl;
            
                info[j] = "Take Line#" + to_string(line[i]) + " from " + //注意是line[i],不是line[j]
                get_number(t) + " to " + get_number(j) + ".";
            }
            else if(d[j] == d[t] + w[i]){
                if(cnt[j] > cnt[t] + 1){//如果是经过边数更少的那一个情况
                    cnt[j] = cnt[t] + 1;
                    pre[j] = t;
                    //info[j] = "Take Line#" + to_string(line[j]) + "from "+ to_string(t) +" to "+ to_string(j) <<endl;
                
                    info[j] = "Take Line#" + to_string(line[i]) + " from " + 
                    get_number(t) + " to " + get_number(j) + ".";
                }
            }
        }
    }

自己的代码

//这道题的关键在于,将一条地铁线路中的每个点都连起来,存成对应边和对应点,这样,一条边就可以代表这一个线路
//在上面的基础上,换成次数最少,就变成了了边数最少
//于是问题变成了求边数最少的最短路问题

//这道题需要用到堆优化,因为点数较多,不可能遍历寻找最小值
//于是使用堆优化去求解
//在存储边数的时候,可以顺便把距离和所属的路线一起存了


#include<iostream>
#include <cstring>
//#include <vector> queue里面好像也有vector ,所以不用重复调用
#include <queue>
#define x first
#define y second
using namespace std;
//const int N = 10001, M = 100000001;;

typedef pair<int, int> PII;
const int N = 10010, M = 1000010;

int n;
int h[N], e[M], w[M], line[M], ne[M], idx;
int d[N], cnt[N], pre[N];
int stops[N];
string info[N];
bool st[N];

string get_number(int x)
{
    char res[5];
    sprintf(res, "%04d", x);
    return res;
}

void add(int a, int b, int c, int id)
{
    e[idx] = b, w[idx] = c, line[idx] = id, ne[idx] = h[a], h[a] = idx ++ ;
}
/*
void dijkstra(int start, int end)
{
    memset(dist, 0x3f, sizeof dist);
    memset(cnt, 0x3f, sizeof cnt);
    memset(st, 0, sizeof st);

    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, start});
    dist[start] = cnt[start] = 0;

    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.y;
        if (ver == end) break;
        if (st[ver]) continue;
        st[ver] = true;

        for (int i = h[ver]; ~i; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[ver] + w[i])
            {
                dist[j] = dist[ver] + w[i];
                cnt[j] = cnt[ver] + 1;
                pre[j] = ver;
                info[j] = "Take Line#" + to_string(line[i]) + " from " + 
                    get_number(ver) + " to " + get_number(j) + ".";
                heap.push({dist[j], j});
            }
            else if (dist[j] == dist[ver] + w[i])
            {
                if (cnt[j] > cnt[ver] + 1)
                {
                    cnt[j] = cnt[ver] + 1;
                    pre[j] = ver;
                    info[j] = "Take Line#" + to_string(line[i]) + " from " + 
                        get_number(ver) + " to " + get_number(j) + ".";
                }
            }
        }
    }

    cout << dist[end] << endl;
    vector<string> path;
    for (int i = end; i != start; i = pre[i])
        path.push_back(info[i]);

    for (int i = path.size() - 1; ~i; i -- )
        cout << path[i] << endl;
}
*/
void dijkstra(int a, int b){

    
    memset(d, 0x3f, sizeof d);//是设置为0x3f
    memset(cnt, 0x3f, sizeof cnt);
    memset(st, 0, sizeof st);

    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, a});
    d[a] = cnt[a] = 0;
    
    while(heap.size()){//由于点较分散,于是我们使用堆优化的方法区进行
        
        auto t = heap.top().second;
        heap.pop();
        
        //st[t] = true;
        if(t == b) break;//这个很关键,堆不一定会变空,所以如果终点确定下来了就退出循环
        if(st[t]) continue;//堆优化,没有对st的判断,所以如果st已经确定了,则直接continue
        
        st[t] = true;
        
        for(int i = h[t]; i != -1; i = ne[i]){
            int j = e[i];
            if(d[j] > d[t] + w[i]){
                d[j] = d[t] + w[i];
                heap.push({d[j], j});
                cnt[j] = cnt[t] + 1;
                pre[j] = t;
                //info[j] = "Take Line#" + to_string(line[j]) + "from "+ to_string(t) +" to "+ to_string(j) <<endl;
            
                info[j] = "Take Line#" + to_string(line[i]) + " from " + //注意是line[i],不是line[j]
                get_number(t) + " to " + get_number(j) + ".";
            }
            else if(d[j] == d[t] + w[i]){
                if(cnt[j] > cnt[t] + 1){//如果是经过边数更少的那一个情况
                    cnt[j] = cnt[t] + 1;
                    pre[j] = t;
                    //info[j] = "Take Line#" + to_string(line[j]) + "from "+ to_string(t) +" to "+ to_string(j) <<endl;
                
                    info[j] = "Take Line#" + to_string(line[i]) + " from " + 
                    get_number(t) + " to " + get_number(j) + ".";
                }
            }
        }
    }
    cout<<d[b]<<endl;//输出距离
    vector<int> path;
    for(int i = b; i != a; i = pre[i]){
        path.push_back(i);
    }
    for(int i = path.size() - 1; i >= 0; i --){
        cout<<info[path[i]]<<endl;
    }
    
}
int main(){
    cin>>n;
    memset(h, -1, sizeof h);
    for(int i = 1; i <= n; i ++){
        int m;
        cin>>m;
        for(int j = 0; j < m; j ++){
            cin>>stops[j];
        }
        for(int j = 0; j < m; j ++){
            for(int k = 0; k < j; k ++){//注意这里有很重要的一点,就是有可能该条线路是一个环,我们要取短的那一条
                int len;
                if(stops[0] != stops[m - 1]) len = j - k;
                else len = min(j - k, m - 1  + k - j);
                add(stops[j], stops[k], len, i);
                add(stops[k], stops[j], len, i);
            }
        }
    }
    int k;
    cin>>k;
    for(int i = 0; i < k; i ++){
        int a, b;
        cin>>a>>b;
        dijkstra(a, b);
    }
}

y总的代码

#include <iostream>
#include <cstring>
#include <queue>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 10010, M = 1000010;

int n;
int h[N], e[M], w[M], line[M], ne[M], idx;
int dist[N], cnt[N], pre[N];
int stops[N];
string info[N];
bool st[N];

string get_number(int x)
{
    char res[5];
    sprintf(res, "%04d", x);
    return res;
}

void add(int a, int b, int c, int id)
{
    e[idx] = b, w[idx] = c, line[idx] = id, ne[idx] = h[a], h[a] = idx ++ ;
}

void dijkstra(int start, int end)
{
    memset(dist, 0x3f, sizeof dist);
    memset(cnt, 0x3f, sizeof cnt);
    memset(st, 0, sizeof st);

    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, start});
    dist[start] = cnt[start] = 0;

    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.y;
        if (ver == end) break;
        if (st[ver]) continue;
        st[ver] = true;

        for (int i = h[ver]; ~i; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[ver] + w[i])
            {
                dist[j] = dist[ver] + w[i];
                cnt[j] = cnt[ver] + 1;
                pre[j] = ver;
                info[j] = "Take Line#" + to_string(line[i]) + " from " + 
                    get_number(ver) + " to " + get_number(j) + ".";
                heap.push({dist[j], j});
            }
            else if (dist[j] == dist[ver] + w[i])
            {
                if (cnt[j] > cnt[ver] + 1)
                {
                    cnt[j] = cnt[ver] + 1;
                    pre[j] = ver;
                    info[j] = "Take Line#" + to_string(line[i]) + " from " + 
                        get_number(ver) + " to " + get_number(j) + ".";
                }
            }
        }
    }

    cout << dist[end] << endl;
    vector<string> path;
    for (int i = end; i != start; i = pre[i])
        path.push_back(info[i]);

    for (int i = path.size() - 1; ~i; i -- )
        cout << path[i] << endl;
}

int main()
{
    cin >> n;
    memset(h, -1, sizeof h);
    for (int i = 1; i <= n; i ++ )
    {
        int m;
        cin >> m;
        for (int j = 0; j < m; j ++ ) cin >> stops[j];

        for (int j = 0; j < m; j ++ )
            for (int k = 0; k < j; k ++ )
            {
                int len;
                if (stops[0] != stops[m - 1]) len = j - k;
                else len = min(j - k, m - 1 - j + k);

                add(stops[j], stops[k], len, i);
                add(stops[k], stops[j], len, i);
            }
    }

    int k;
    cin >> k;
    while (k -- )
    {
        int start, end;
        cin >> start >> end;
        dijkstra(start, end);
    }

    return 0;
}


作者:yxc
链接:https://www.acwing.com/activity/content/code/content/295487/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

顶点覆盖

甲级1134

题目

如果图中的一个顶点集合能够满足图中的每一条边都至少有一个端点在该集合内,那么这个顶点集合就是图的顶点覆盖。

现在给定一张图,以及若干个顶点集合,请你判断这些顶点集合是否是图的顶点覆盖。

输入格式
第一行包含两个整数 N 和 M,表示图中点和边的数量。

接下来 M 行,每行包含两个整数 a,b,表示点 a 和点 b 之间存在一条边。

点编号 0∼N−1。

然后包含一个整数 K,表示询问的顶点集合数量。

接下来 K 行,每行描述一组询问顶点集合,格式如下:

Nv V[1] V[2] … V[Nv]
Nv 是集合中点的数量,V[i] 是点的编号。

输出格式
每组询问输出一行结果,如果是顶点覆盖则输出 Yes,否则输出 No。

数据范围
1≤N,M≤104,
1≤K≤100,
1≤Nv≤N
输入样例:
10 11
8 7
6 8
4 5
8 4
8 1
1 2
1 4
9 8
9 1
1 0
2 4
5
4 0 3 8 4
6 6 1 7 5 4 9
3 1 8 4
2 2 8
7 9 8 7 6 5 4 2
输出样例:
No
Yes
Yes
No
No

思想

用struct存储边
如果需要遍历所有边

自己的代码

#include<iostream>
#include <cstring>

using namespace std;
const int N = 1e4 + 10;
struct Node{
    int a, b;
}e[N];
bool st[N];
int main(){
    int n, m, k, t;
    cin>>n>>m;
    for(int i = 0; i < m; i ++){
        int a, b;
        cin>>a>>b;
        e[i].a = a, e[i].b = b;
    }
    cin>>k;
    for(int i = 0; i < k; i ++){
        cin>>t;
        int cnt = 0;

        memset(st, 0, sizeof st);
        
        for(int j = 0; j < t; j ++){
            int a;
            cin>>a;
            st[a] = true;
        }
        for(int j = 0; j < m; j ++){
            if(st[e[j].a] || st[e[j].b])
                cnt ++;
        }

        if(cnt == m) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
}

y总的代码

#include <iostream>
#include <cstring>

using namespace std;

const int N = 10010;

int n, m;
struct Edge
{
    int a, b;
}e[N];
bool st[N];

int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; i ++ ) cin >> e[i].a >> e[i].b;

    int k;
    cin >> k;
    while (k -- )
    {
        int cnt;
        cin >> cnt;

        memset(st, 0, sizeof st);
        while (cnt -- )
        {
            int x;
            cin >> x;
            st[x] = true;
        }

        int i;
        for (i = 0; i < m; i ++ )
            if (!st[e[i].a] && !st[e[i].b])
                break;

        if (i == m) puts("Yes");
        else puts("No");
    }

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/295498/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

第一次接触

题目

思想

自己的代码

y总的代码

最大集团

甲级1142

题目

在一个无向图中,如果一个顶点子集满足子集内的任意两个不同顶点之间都是相连的,那么这个顶点子集就被称为一个团。

如果一个团不能通过加入某个新的顶点来扩展成一个更大的团,那么该团就被称为最大团。

现在,你需要判断给定顶点子集能否构成一个最大团。

输入格式
第一行包含两个整数 Nv 和 Ne,分别表示无向图中点和边的数量。

接下来 Ne 行,每行包含两个整数 a,b,表示点 a 和点 b 之间存在一条边。

所有点的编号从 1 到 Nv。

再一行,包含整数 M,表示询问次数。

接下来 M 行,每行描述一个询问顶点子集,首先包含一个整数 K,表示子集包含点的数量,然后包含 K 个整数,表示 K 个不同顶点的编号。

一行中所有数字之间用一个空格隔开。

输出格式
每组询问在一行中输出一个结论。

如果给定子集是最大团,则输出 Yes,如果是一个团,但不是最大团,则输出 Not Maximal,如果根本不是团,则输出 Not a Clique。

数据范围
1≤Nv≤200,
1≤Ne≤Nv(Nv−1)2,
1≤M≤100,
1≤K≤Nv
输入样例:
8 10
5 6
7 8
6 4
3 6
4 5
2 3
8 2
2 7
5 3
3 4
6
4 5 4 3 6
3 2 8 7
2 2 3
1 1
3 4 3 6
3 3 2 1
输出样例:
Yes
Yes
Yes
Yes
Not Maximal
Not a Clique

思想

就是题目的意思去写就可了

自己的代码

#include <iostream>
using namespace std;
const int N = 210;
int g[N][N], q[N];
int main(){
    int n, m, k, t;
    cin>>n>>m;
    for(int i = 0; i < m; i ++){
        int a, b;
        cin>>a>>b;
        g[a][b] = 1;
        g[b][a] = 1;
    }
    cin>>k;
    for(int i = 0; i < k; i ++){
        cin>>t;
        for(int j = 0; j < t; j ++) cin>>q[j];
        
        bool flag = true;
        for(int j = 0; j < t; j ++){
            for(int p = 0; p < t; p ++){
                //cout<<q[j]<<" "<<q[p]<<" "<<g[q[j]][q[p]]<<endl;
                if(p != j && !g[q[j]][q[p]]){
                    flag = false;
                    break;
                }
            }
            if(!flag) break;
        }
        int cnt = 0;
        if(flag){
            for(int j = 1; j <= n; j ++){
                for(int p = 0; p < t; p ++){
                    if(q[p] != j && !g[j][q[p]]){
                        cnt++;
                        break;
                    }
                }
            }
            if(cnt == n - t) cout<<"Yes"<<endl;
            else cout<<"Not Maximal"<<endl;
        }
        else{
            cout<<"Not a Clique"<<endl;
        }
    }
}

y总的代码

#include <iostream>
#include <cstring>

using namespace std;

const int N = 210;

int n, m;
bool g[N][N], st[N];
int vers[N];

bool check_clique(int cnt)
{
    for (int i = 0; i < cnt; i ++ )
        for (int j = 0; j < i; j ++ )
            if (!g[vers[i]][vers[j]])
                return false;
    return true;
}

bool check_maximum(int cnt)
{
    memset(st, 0, sizeof st);
    for (int i = 0; i < cnt; i ++ )
        st[vers[i]] = true;

    for (int i = 1; i <= n; i ++ )
        if (!st[i])
        {
            bool success = true;
            for (int j = 0; j < cnt; j ++ )
                if (!g[i][vers[j]])
                {
                    success = false;
                    break;
                }

            if (success) return false;
        }

    return true;
}

int main()
{
    cin >> n >> m;
    while (m -- )
    {
        int a, b;
        cin >> a >> b;
        g[a][b] = g[b][a] = true;
    }

    int k;
    cin >> k;
    while (k -- )
    {
        int cnt;
        cin >> cnt;
        for (int i = 0; i < cnt; i ++ ) cin >> vers[i];
        if (check_clique(cnt))
        {
            if (check_maximum(cnt)) puts("Yes");
            else puts("Not Maximal");
        }
        else puts("Not a Clique");
    }

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/295529/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

拓扑排序

题目

这是 2018 年研究生入学考试中给出的一个问题:

以下哪个选项不是从给定的有向图中获得的拓扑序列?

现在,请你编写一个程序来测试每个选项。

5d35ed2a-4d19-4f13-bf3f-35ed59cebf05.jpg

输入格式
第一行包含两个整数 N 和 M,分别表示有向图的点和边的数量。

接下来 M 行,每行给出一条边的起点和终点。

点的编号从 1 到 N。

再一行包含一个整数 K,表示询问次数。

接下来 K 行,每行包含一个所有点的排列。

一行中的数字用空格隔开。

输出格式
在一行中输出所有不是拓扑序列的询问序列的编号。

询问序列编号从 0 开始。

行首和行尾不得有多余空格,保证存在至少一个解。

数据范围
1≤N≤1000,
1≤M≤10000,
1≤K≤100
输入样例:
6 8
1 2
1 3
5 2
5 4
2 3
2 6
3 4
6 4
5
1 5 2 3 6 4
5 1 2 6 3 4
5 1 2 3 6 4
5 2 1 6 3 4
1 2 3 4 5 6
输出样例:
3 4

思想

待更新y总代码思路

自己的代码

自己主要是去模拟整个拓扑排序的过程去判断的

#include <iostream>
#include <vector>
using namespace std;
const int N = 1e4 + 10;
int g[N][N], a[N], n;
bool check(vector<int> d){
    
    for(int i = 0; i < n; i ++){
        if(d[a[i]] == 0){
            for(int j = 1; j <= n; j ++){
                if(g[a[i]][j]){
                    d[j]--;
                }
            }
        }
        else return false;
    }
    return true;
}
int main(){
    int m;
    cin>>n>>m;
    vector<int> d(N);
    for(int i = 0; i < m; i ++){
        int a, b;
        scanf("%d %d", &a, &b);
        g[a][b] = 1;
        d[b] ++;
    }
    int k;
    cin>>k;
    bool is_first = true;
    for(int i = 0; i < k; i ++){
        vector<int> temp = d;
        for(int j = 0; j < n; j ++)
            scanf("%d", &a[j]);
        
        if(!check(temp)){
            if(is_first) is_first = false;
            else printf(" ");
            printf("%d", i);
        }
        
    }
}

y总的代码

#include <iostream>
#include <cstring>

using namespace std;

const int N = 1010, M = 10010;

int n, m;
struct Edge
{
    int a, b;
}e[M];
int p[N];

int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; i ++ ) cin >> e[i].a >> e[i].b;

    int k;
    cin >> k;

    bool is_first = true;
    for (int i = 0; i < k; i ++ )
    {
        for (int j = 1; j <= n; j ++ )
        {
            int x;
            cin >> x;
            p[x] = j;
        }

        bool success = true;
        for (int j = 0; j < m; j ++ )
            if (p[e[j].a] > p[e[j].b])
            {
                success = false;
                break;
            }

        if (!success)
        {
            if (is_first) is_first = false;
            else cout << ' ';
            cout << i;
        }
    }

    cout << endl;

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/295539/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

旅行商问题

题目

“旅行商问题”是这样一个问题:“给出一个城市列表以及每对城市之间的距离,访问每个城市并返回原城市的最短路线是什么?”

这是组合优化中的一个 NP 难题,在运筹学和理论计算机科学中十分重要。

在此问题中,请你从给定的路径列表中找到最接近旅行商问题的解的路径。

输入格式
第一行包含两个整数 N 和 M,分别表示城市数量以及 无向图 中边的数量。

接下来 M 行,每行以 City1 City2 Dist 格式描述一条边,其中城市编号从 1 到 N,Dist 为正且不超过 100。

再一行包含一个整数 K,表示给定路径的数量。

接下来 K 行描述路径,格式为:

n C1 C2 … Cn
n 表示给定路径经过的城市的数目,Ci 是路径中经过的城市的编号。

输出格式
对于每个路径,在一行中输出 Path X: TotalDist (Description)。

其中 X 是路径编号(从 1 开始),TotalDist 表示路径总距离(如果距离不存在,则输出 NA),Description 是下列中的一项:

TS simple cycle,如果这是一个访问每个城市的简单回路。
TS cycle,如果这是一个访问每个城市的回路,但不是简单回路。
Not a TS cycle,如果这不是一个访问了每个城市的回路。
最后一行,输出 Shortest Dist(X) = TotalDist,X 是最接近旅行商问题解决方案的回路编号,TotalDist 是其总距离。

保证有唯一解。

数据范围
2<N≤200,
N−1≤M≤N(N−1)2,
1≤K≤1000,
1≤n≤300
输入样例:
6 10
6 2 1
3 4 1
1 5 1
2 5 1
3 1 8
4 1 6
1 6 1
6 3 1
1 2 1
4 5 1
7
7 5 1 4 3 6 2 5
7 6 1 3 4 5 2 6
6 5 1 4 3 6 2
9 6 2 1 6 3 4 5 2 6
4 1 2 5 1
7 6 1 2 5 4 3 1
7 6 3 2 5 4 1 6
输出样例:
Path 1: 11 (TS simple cycle)
Path 2: 13 (TS simple cycle)
Path 3: 10 (Not a TS cycle)
Path 4: 8 (TS cycle)
Path 5: 3 (Not a TS cycle)
Path 6: 13 (Not a TS cycle)
Path 7: NA (Not a TS cycle)
Shortest Dist(4) = 8

思想

简单回路是只经过一次每个点
而非简单回路就是可能经过多次

自己的代码

#include <iostream>
#include <unordered_map>
#include <cstring>
using namespace std;
const int N = 1e3 + 10;
int g[N][N], a[N];
int main(){
    int n, m;
    cin>>n>>m;
    memset(g, 0x3f, sizeof g);
    for(int i = 0; i < m; i ++){
        int a, b, distance;
        cin>>a>>b>>distance;
        g[a][b] = distance;
        g[b][a] = distance;
    }
    int k;
    cin>>k;
    int min_res = 0x3f3f3f3f, min_index = 0;
    for(int i = 0; i < k; i ++){
        int t;
        cin>>t;
        int num = 0, sum = 0;
        bool flag2 = false;
        unordered_map<int, int> hash;

        for(int j = 0;j < t; j ++){
            cin>>a[j];
            if(!hash.count(a[j])){
                hash[a[j]] = 1;
                num++;
            }
            else{
                hash[a[j]] ++;
                if(j != t - 1) flag2 = true;
            }
            if(j && g[a[j]][a[j - 1]] == 0x3f3f3f3f){
                sum = 0x3f3f3f3f;
            }
            else{
                if(j) sum += g[a[j]][a[j - 1]];
            }
        }
        

        if(num == n && a[0] == a[t - 1] && sum < 0x3f3f3f3f){
            printf("Path %d: %d", i + 1, sum);
            if(!flag2) printf(" (TS simple cycle)\n");
            else printf(" (TS cycle)\n");
            
            if(sum < min_res) min_res = sum, min_index = i + 1;
        }
        else{
            if(sum >= 0x3f3f3f3f) printf("Path %d: NA", i + 1);
            else printf("Path %d: %d", i + 1, sum);
            printf(" (Not a TS cycle)\n");
        }
    }
    printf("Shortest Dist(%d) = %d", min_index, min_res);
}

y总的代码

#include <iostream>
#include <cstring>

using namespace std;

const int N = 210, INF = 0x3f3f3f3f;

int n, m;
int d[N][N], vers[310];
bool st[N];

int main()
{
    cin >> n >> m;
    memset(d, 0x3f, sizeof d);
    for (int i = 0; i < m; i ++ )
    {
        int a, b, c;
        cin >> a >> b >> c;
        d[a][b] = d[b][a] = c;
    }

    int k;
    cin >> k;

    int min_dist = INF, min_id;
    for (int T = 1; T <= k; T ++ )
    {
        int cnt;
        cin >> cnt;
        for (int i = 0; i < cnt; i ++ ) cin >> vers[i];

        int sum = 0;
        bool success = true;
        memset(st, 0, sizeof st);
        for (int i = 0; i + 1 < cnt; i ++ )
        {
            int a = vers[i], b = vers[i + 1];
            if (d[a][b] == INF)
            {
                sum = -1;
                success = false;
                break;
            }
            else sum += d[a][b];
            st[a] = true;
        }

        for (int i = 1; i <= n; i ++ )
            if (!st[i])
            {
                success = false;
                break;
            }

        if (vers[0] != vers[cnt - 1]) success = false;

        if (sum == -1) printf("Path %d: NA (Not a TS cycle)\n", T);
        else
        {
            if (!success) printf("Path %d: %d (Not a TS cycle)\n", T, sum);
            else
            {
                if (cnt == n + 1) printf("Path %d: %d (TS simple cycle)\n", T, sum);
                else printf("Path %d: %d (TS cycle)\n", T, sum);

                if (min_dist > sum)
                {
                    min_dist = sum;
                    min_id = T;
                }
            }
        }
    }

    printf("Shortest Dist(%d) = %d\n", min_id, min_dist);

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/295563/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

顶点着色

题目

一个合适的顶点着色是指用各种颜色标记图中各个顶点,使得每条边的两个端点的颜色都不相同。

如果一种合适的顶点着色方案使用了一共 k 种不同的颜色,则称其为合适的 k 着色(k-coloring)。

现在,你需要判断给定的着色方案是否是合适的 k 着色方案。

输入格式
第一行包含两个整数 N 和 M,分别表示点和边的数量。

接下来 M 行,每行包含两个整数 a,b,表示点 a 和点 b 之间存在一条边。

所有点的编号从 0 到 N−1。

再一行包含一个整数 K,表示你需要判断的着色方案。

接下来 K 行,每行包含 N 个颜色,其中第 i 个颜色表示第 i 个点的颜色。

颜色用非负整数表示,不超过 int 范围。

输出格式
对于每种着色方案,如果是一种合适的 k 着色方案,则输出一行 k-coloring。

如果不是合适的着色方案,则输出一行 No。

数据范围
1≤N,M≤104,
1≤K≤100
输入样例:
10 11
8 7
6 8
4 5
8 4
8 1
1 2
1 4
9 8
9 1
1 0
2 4
4
0 1 0 1 4 1 0 1 3 0
0 1 0 1 4 1 0 1 0 0
8 1 0 1 4 1 0 5 3 0
1 2 3 4 5 6 7 8 8 9
输出样例:
4-coloring
No
6-coloring
No
难度:简单
时/空限制:0.9s / 64MB
总通过数:354
总尝试数:716
来源:PAT甲级真题1154
算法标签

思想

这道题不需要去复杂化去进行染色图二分,直接判断每条边的不相等就可以了

自己的代码

//把问题复杂化了,不需要去用dfs,只需要去判断每条边是否是不相同的就可以了

#include <iostream>
#include <cstring>
#include <unordered_set>

using namespace std;

const int N = 10010;

int n, m;
struct Edge
{
    int a, b;
}e[N];
int color[N];

int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; i ++ ) cin >> e[i].a >> e[i].b;

    int k;
    cin >> k;
    while (k -- )
    {
        for (int i = 0; i < n; i ++ ) cin >> color[i];

        bool success = true;
        for (int i = 0; i < m; i ++ )
            if (color[e[i].a] == color[e[i].b])
            {
                success = false;
                break;
            }

        if (success)
        {
            unordered_set<int> S;
            for (int i = 0; i < n; i ++ ) S.insert(color[i]);
            printf("%d-coloring\n", S.size());
        }
        else puts("No");
    }

    return 0;
}




/*#include <iostream>
#include <unordered_set>
#include <cstring>
using namespace std;
const int N = 1e4 + 10;
int st[N], h[N], e[N * N], ne[N * N],c[N], idx = 0;
int cnt = 0;
unordered_set<int> hash_set;
bool dfs(int u){
    st[u] = true;
    if(!hash_set.count(c[u])){
        cnt++;
        hash_set.insert(c[u]);
    }
    for(int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if(!st[j]){
           if(c[j] == c[u]) {
                return false;
            }
            if(!dfs(j)) return false;
        }
    }
    return true;
}
void add(int a, int b){
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx;
    idx++;
}
int main(){
    int n, m;
    cin>>n>>m;
    memset(h, -1, sizeof h);
    for(int i = 0; i < m; i ++){
        int a, b;
        cin>>a>>b;
        add(a, b);
        add(b, a);
    }
    int k;
    cin>>k;
    for(int i = 0; i < k; i ++){
        memset(c, 0, sizeof c);
        memset(st, 0, sizeof st);
        for(int j = 0; j < n; j ++)
            cin>>c[j];
        cnt = 0;
        hash_set.clear();
        bool flag = true;
        for(int j = 0; j < n; j ++){
            if(!st[j]){
                flag = dfs(j);
                if(!flag) break;
            }
        }
        if(!flag)cout<<"No"<<endl;
        else{
            cout<<cnt<<"-coloring"<<endl;
        }
    }
    
}*/

y总的代码

#include <iostream>
#include <cstring>
#include <unordered_set>

using namespace std;

const int N = 10010;

int n, m;
struct Edge
{
    int a, b;
}e[N];
int color[N];

int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; i ++ ) cin >> e[i].a >> e[i].b;

    int k;
    cin >> k;
    while (k -- )
    {
        for (int i = 0; i < n; i ++ ) cin >> color[i];

        bool success = true;
        for (int i = 0; i < m; i ++ )
            if (color[e[i].a] == color[e[i].b])
            {
                success = false;
                break;
            }

        if (success)
        {
            unordered_set<int> S;
            for (int i = 0; i < n; i ++ ) S.insert(color[i]);
            printf("%d-coloring\n", S.size());
        }
        else puts("No");
    }

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/295574/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

扩展——二分图

int n;      // n表示点数
int h[N], e[M], ne[M], idx;     // 邻接表存储图
int color[N];       // 表示每个点的颜色,-1表示未染色,0表示白色,1表示黑色

// 参数:u表示当前节点,c表示当前点的颜色
bool dfs(int u, int c)
{
    color[u] = c;
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (color[j] == -1)
        {
            if (!dfs(j, !c)) return false;
        }
        else if (color[j] == c) return false;
    }

    return true;
}

bool check()
{
    memset(color, -1, sizeof color);
    bool flag = true;
    for (int i = 1; i <= n; i ++ )
        if (color[i] == -1)
            if (!dfs(i, 0))
            {
                flag = false;
                break;
            }
    return flag;
}

作者:yxc
链接:https://www.acwing.com/blog/content/405/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

公共自行车管理

题目

思想

自己的代码

y总的代码

加油站

题目

加油站的建造位置必须使加油站与距离它最近的房屋的距离尽可能远。

与此同时,它还必须保证所有房屋都在其服务范围内。

现在,给出了城市地图和加油站的几个候选位置,请你提供最佳建议。

如果有多个解决方案,请输出选取位置与所有房屋的平均距离最小的解决方案。

如果这样的解决方案仍然不是唯一的,请输出选取位置编号最小的解决方案。

输入格式
第一行包含四个整数 N,房屋总数,M,加油站的候选位置总数,K,连接房屋或加油站的道路总数,Ds 加油站的最大服务范围。

所有房屋的编号从 1 到 N,所有加油站侯选位置编号从 G1 到 GM。

接下来 K 行,每行格式如下:

P1 P2 Dist
其中,P1 和 P2 表示一条 无向 道路连接的两个房屋或加油站侯选位置的编号,Dist 是道路长度,这是一个整数。

输出格式
第一行输出所选位置的编号。

第二行输出加油站与距离其最近的房屋之间的距离以及与所有房屋之间的平均距离,精确到小数后一位。

如果解决方案不存在,则输出 No Solution。

数据范围
1≤N≤103,
1≤M≤10,
1≤K≤104,
1≤Ds≤200,
1≤Dist≤5000
输入样例1:
4 3 11 5
1 2 2
1 4 2
1 G1 4
1 G2 3
2 3 2
2 G2 1
3 4 2
3 G3 2
4 G1 3
G2 G1 1
G3 G2 2
输出样例1:
G1
2.0 3.3
输入样例2:
2 1 2 10
1 G1 9
2 G1 20
输出样例2:
No Solution

思想

自己的代码

//在调试中遇到的
//注意用户G是可能等于10的,所以在进行转换的时候,不能只考虑样例的数据,要考虑给的取值范围

#include<iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
struct station{
    string id;
    double nearest;
    double ave;
    bool operator < (const station &s) const{
        if(nearest != s.nearest) return nearest > s.nearest;
        if(ave != s.ave) return ave < s.ave;
        return id < s.id; 
    }
};
const int N = 1e3+ 30;
int g[N][N], dist[N];
bool st[N];
vector<station> record;
int n, m, k, ds;
int convert(string a){
    if(a[0] == 'G'){
        if(a.size() == 2)
            return a[1] - '0' + n;
        else
            return n + 10;
    }
    else{
        int sum = 0;
        for(int i = 0; i < a.size(); i ++)
            sum = sum * 10 + a[i] - '0';
        return sum;
    }
}
string decode(int num){
    if(num > n) return "G" + to_string(num - n);
    else return to_string(num);
}
void djkstra(int start){
    
    memset(dist, 0x3f, sizeof dist);
    memset(st, 0, sizeof st);

    dist[start] = 0;
    for (int i = 0; i < n + m; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n + m; j ++ )
            if (!st[j] && (t == -1 || dist[j] < dist[t]))
                t = j;

        st[t] = true;

        for (int j = 1; j <= n + m; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);
    }


    int sum = 0;
    bool flag = true;
    int res = 0x3f3f3f3f;
    for(int i = 1; i <= n; i ++){
        if(dist[i] > ds) {
            flag = false;
            break;
        }
        sum += dist[i];
        if(dist[i] < res) res = dist[i];
    }
    if(flag){
        double ave = (double) sum / n + 1e-8;
        double nearest = res;
        record.push_back({decode(start), nearest, ave});
    }
}

int main(){
    int i, j;
    cin>>n>>m>>k>>ds;
    memset(g, 0x3f, sizeof g);
    for(i = 0; i < k; i ++){
        int distance;
        string a, b;
        cin>>a>>b>>distance;
        g[convert(a)][convert(b)] = distance;
        g[convert(b)][convert(a)] = distance;
    }
    for(i = 1; i <= m; i ++){
        string a = "G" + to_string(i);
        djkstra(convert(a));
    }
    sort(record.begin(), record.end());
    if(record.size()){
        cout<<record[0].id<<endl;
        printf("%.1lf %.1lf\n", record[0].nearest, record[0].ave);}//发现系统对于3.25保留一位小数是3.2,所以对于这种情况我们需要加一个1e-8
    else
        cout<<"No Solution"<<endl;
}

y总的代码

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1020, INF = 0x3f3f3f3f;

int n, m, k, D;
int g[N][N];
int dist[N];
bool st[N];

int get(string s)
{
    if (s[0] == 'G') return n + stoi(s.substr(1));
    return stoi(s);
}

void dijkstra(int start, int &mind, int &sumd)
{
    memset(dist, 0x3f, sizeof dist);
    memset(st, 0, sizeof st);

    dist[start] = 0;
    for (int i = 0; i < n + m; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n + m; j ++ )
            if (!st[j] && (t == -1 || dist[j] < dist[t]))
                t = j;

        st[t] = true;

        for (int j = 1; j <= n + m; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);
    }

    for (int i = 1; i <= n; i ++ )
        if (dist[i] > D)
        {
            mind = -INF;
            return;
        }

    mind = INF, sumd = 0;
    for (int i = 1; i <= n; i ++ )
    {
        mind = min(mind, dist[i]);
        sumd += dist[i];
    }
}

int main()
{
    cin >> n >> m >> k >> D;

    memset(g, 0x3f, sizeof g);
    while (k -- )
    {
        string a, b;
        int z;
        cin >> a >> b >> z;
        int x = get(a), y = get(b);

        g[x][y] = g[y][x] = min(g[x][y], z);
    }

    int res = -1, mind = 0, sumd = INF;
    for (int i = n + 1; i <= n + m; i ++ )
    {
        int d1, d2;
        dijkstra(i, d1, d2);

        if (d1 > mind) res = i, mind = d1, sumd = d2;
        else if (d1 == mind && d2 < sumd) res = i, sumd = d2;
    }

    if (res == -1) puts("No Solution");
    else printf("G%d\n%.1lf %.1lf\n", res - n, (double)mind, (double)sumd / n + 1e-8);

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/323579/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

微博转发

题目

微博被称为中文版的 Twitter。

微博上的用户既可能有很多关注者,也可能关注很多其他用户。

因此,形成了一种基于这些关注关系的社交网络。

当用户在微博上发布帖子时,他/她的所有关注者都可以查看并转发他/她的帖子,然后这些人的关注者可以对内容再次转发…

现在给定一个社交网络,假设只考虑 L 层关注者,请你计算某些用户的帖子的最大可能转发量。

补充
如果 B 是 A 的关注者,C 是 B 的关注者,那么 A 的第一层关注者是 B,第二层关注者是 C。

输入格式
第一行包含两个整数,N 表示用户数量,L 表示需要考虑的关注者的层数。

假设,所有的用户的编号为 1∼N。

接下来 N 行,每行包含一个用户的关注信息,格式如下:

M[i] user_list[i]
M[i] 是第 i 名用户关注的总人数,user_list[i] 是第 i 名用户关注的 M[i] 个用户的编号列表。

最后一行首先包含一个整数 K,表示询问次数,然后包含 K 个用户编号,表示询问这些人的帖子的最大可能转发量。

输出格式
按顺序,每行输出一个被询问人的帖子最大可能转发量。

假设每名用户初次看到帖子时,都会转发帖子,只考虑 L 层关注者。

数据范围
1≤N≤1000,
1≤L≤6,
1≤M[i]≤100,
1≤K≤N
输入样例:
7 3
3 2 3 4
0
2 5 6
2 3 1
2 3 4
1 4
1 5
2 2 6
输出样例:
4
5
难度:中等
时/空限制:3s / 64MB
总通过数:327
总尝试数:817
来源:PAT甲级真题1076
算法标签

思想

记录所在层的层次遍历

第一种写法——自己的代码
第二种写法——y总的代码

自己的代码

//y总答案也记录了一种层次遍历的写法!

#include<iostream>
#include<cstring>
using namespace std;
const int N = 1010;
bool g[N][N];
int n, l;
int layer(int root){
    int q[N];
    bool st[N];
    memset(st, 0, sizeof st);//注意一定要重新赋值!!!!
    int hh = 0, tt = -1;
    q[++tt] = root;
    st[root] = true;
    int lay = 1, cnt = 0;
    while(hh <= tt){
        int tail = tt;
        if(lay > l)
            break;
        while(hh <= tail){
            int t = q[hh++];
            for(int j = 1; j <= n; j ++){
                if(g[t][j] && !st[j]){
                    q[++tt] = j;
                    cnt++;
                    st[j] = true;
                }
            }
        }
        lay++;
    }
    return cnt;
}
int main(){
    cin>>n>>l;
    for(int i = 1; i <= n; i ++){
        int m;
        cin>>m;
        for(int j = 0; j < m; j ++){
            int t;
            cin>>t;
            g[t][i] = true;
        }
    }
    int k, q;
    cin>>k;
    for(int i = 0; i < k; i ++){
        cin>>q;
        cout<<layer(q)<<endl;
    }
    
}

y总的代码


int bfs(int start)
{
    queue<int> q;
    memset(st, 0, sizeof st);

    q.push(start);
    st[start] = true;

    int res = 0;
    for (int step = 0; step < m; step ++ )
    {
        int sz = q.size();
        res += sz;

        for (int i = 0; i < sz; i ++ )
        {
            int t = q.front();
            q.pop();

            for (int j = h[t]; ~j; j = ne[j])
            {
                int k = e[j];
                if (!st[k])
                {
                    st[k] = true;
                    q.push(k);
                }
            }
        }
    }

    return res + q.size() - 1;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/324375/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

第一次接触

题目

思想

自己的代码

y总的代码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值