【无标题】

文章详细介绍了图算法中的Dijkstra、Floyd和SPFA等最短路算法,以及拓扑排序在特定问题中的应用,如处理名字排序。同时,提到了欧拉路径、裴蜀定理和最小生成树的概念,并给出了实际题目来练习这些概念。此外,还讨论了如何利用SPFA判断负环以及二分查找与BFS在求解最大拥挤度最小问题中的应用。最后,文章通过一个实例展示了如何运用裴蜀定理解决特定的数学问题。
摘要由CSDN通过智能技术生成

总体:

7.3
最短路训练:熟悉Dijkstra,floyed,spfa,最长路,差分约束,负环判断(spfa)
7.4
拓扑排序(强化),欧拉路径,裴蜀定理,最小生成树,
7.5
st表,(基础)线段树,树状数组回顾,最小生成树训练

以下为遇到的感觉挺好的题

拓扑排序(强化):在不破坏结点的先后顺序的情况下,把DAG拉成一条链
Kahn算法:不断删入度为0的点,返回值为是否存在环,若题目需要的字典序最小方案,将queue改为priority_queue(优先队列即可)
(CF510C Fox And Names)


题意:给定一串名字,改变26个字母表的顺序,使得名字按字母表的顺序排序
思路:因为拓扑排序是不改变结点的先后顺序情况下把DAG拉成一条链,所以可以比较相邻的两个名字,找到第一个不同处,
连边,然后跑拓扑排序就可以了,如果存在环,则impossible,巨坑的一个点,调了好久啊,子序列必须在前面

 

#include<iostream>
#include<queue>
#include<math.h>
#define ms(x,y) memset(x,y,sizeof x);
#define YES cout<<"YES"<<'\n';
#define NO  cout<<"NO"<<'\n';
#define fr(i,z,n) for(int i = z;i <= n; i++)
#define ufr(i,n,z) for(int i = n;i >= z; i--)
typedef long long ll;
const ll maxn = 2e5 + 10, inf = 1e18;
const ll mod = 1e9 + 7;
int i, j;
using namespace std;
string  a[maxn];
char A[maxn];
struct edge {
    int v, w, next;
}e[maxn];
int cnt = 0; int head[maxn];
int in[maxn];
bool vis[maxn];
void insert(int u, int v, int w) {
    cnt++;
    e[cnt].v = v;
    e[cnt].next = head[u];
    head[u] = cnt;
}
bool  tuopu(int n) {  //拓扑排序
    int cnt1 = 0;
    queue<int>q;
    for (int i = 1; i <= n; i++) {
        if (!in[i]) {
            q.push(i);
            vis[i] = 1;
            A[++cnt1] = i + 'a' - 1;
            //cout << A[cnt1] << ' ';
        }
    }
    //cout << '\n';
    while (!q.empty()) {
        int now = q.front();
        q.pop();
        if (!vis[now]) {
            A[++cnt1] = now + 'a' - 1;
            vis[now] = 1;
        }
        for (int i = head[now]; i; i = e[i].next) {
            int v = e[i].v;
            in[v]--;
            if (!in[v]) {
                q.push(v);
                //cout << A[cnt1] << ' ';
            }
        }
    }
    //cout << cnt1 << '\n';
    return n == cnt1;
}
void solve() {
    int n;
    cin >> n;
    fr(i, 1, n) {
        cin >> a[i];
    }
    fr(i, 2, n) {
        int flag = -1;
        fr(j, 0, min(a[i].size()-1, a[i - 1].size()-1)) {
            if (a[i][j] != a[i - 1][j]) {
                flag = j;
                break;
            }
        }
        if (flag != -1) {
            insert(a[i-1][flag] - 'a' + 1, a[i][flag] - 'a' + 1, 1);
            in[a[i][flag] - 'a' + 1]++;
        }
        if (flag==-1 && a[i - 1] != a[i] && a[i].length() < a[i - 1].length())
        {
            cout << "Impossible" << endl;
            return ;
        }
    }
    
    if (tuopu(26)) {
        for (int i = 1; i <= 26; i++) {
            cout << A[i];
        }
    }
    else {
        cout << "Impossible" << '\n';
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    int t = 1;
    while (t--) {
        solve();
    }
}

P1396 营救


题意:给定n个区,m条道路,起点s,终点t,使s到t的最大拥挤度最小,求最大拥挤度
思路:因为是使得最大拥挤度最小,可以将spfa求最短路的比较策略更改,
 变成比较终点的距离与max(起点,边权)因为起点要到终点必须经过边权,所以必须取max,终点的距离<max则更新
 这道题二分+bfs也可以做,因为关键字最大的最小太明显了
 总结:一般都是求最短路,或是求路径上最大值最小,这道题可以作为(求路径上最大值最小模板题)

 

#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<string.h>
#define ms(x,y) memset(x,y,sizeof x);
#define YES cout<<"YES"<<'\n';
#define NO  cout<<"NO"<<'\n';
#define fr(i,z,n) for(int i = z;i <= n; i++)
#define ufr(i,n,z) for(int i = n;i >= z; i--)
typedef long long ll;
const ll maxn = 2e6 + 10, inf = 1e18;
const ll mod = 1100003;
using namespace std;
int n, m,s,t;
int head[maxn];
int size1;
bool vis[maxn];
int dis[maxn];
int in[maxn];
struct edge {
    int v, w, next;
    edge() {};
    edge(int _v, int _w, int _next) {
        v = _v;
        w = _w;
        next = _next;
    }
}e[maxn * 2];
void init() {
    memset(head, -1, sizeof(head));
    size1 = 0;;
}
void insert(int u, int v, int w) {
    e[size1] = edge(v, w, head[u]);
    head[u] = size1++;
}
bool spfa(int u) {      //spfa板子
    memset(vis, false, sizeof(vis));
    vis[u] = true;
    memset(dis, 0x3f, sizeof(dis));
    //memset(dis, -0x3f, sizeof(dis));
    dis[u] = 0;
    memset(in, 0, sizeof(in));
    queue<int>q;
    q.push(u);
    while (!q.empty()) {
        u = q.front();
        q.pop();
        vis[u] = false;
        for (int j = head[u]; ~j; j = e[j].next) {
            int v = e[j].v;
            int w = e[j].w;
            if (dis[v] > max(dis[u] , w)) {
                //if (dis[v] < dis[u] + w) {
                dis[v] = max(dis[u] , w);
                if (!vis[v]) {
                    q.push(v);
                    vis[v] = true;
                    ++in[v];
                    if (in[v] > n) {
                        //cout << in[v] << '\n';
                        return true;             //有负环
                    }
                }
            }

        }
    }
    return false;
}
void solve() {
    init();
    cin >> n >> m>>s>>t;
    fr(i, 1, m) {
        int a, b, c;
        cin >> a >> b >> c;
        insert(a, b, c);
        insert(b, a, c);
    }
    spfa(s);
    cout << dis[t] << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    int t = 1;
    //cin>>t;
    while (t--) {
        solve();
    }
}

P4549 【模板】裴蜀定理


题意:给定n个整数A,求另一个整数序列X,使得S=Ai*Xi(i从1到n)S>0并且S尽可能小,求S
裴蜀定理:ax+by=c(x,y为正整数且gcd(a,b)|c),将其两个变量推广到多个变量,S尽可能小,
因为每次求的是满足条件的最小情况,所以满足
总结:这道题虽然只是个模板题,但感觉定理挺叼的,记录一下

 

#include<bits/stdc++.h>
#define ms(x,y) memset(x,y,sizeof x);
#define YES cout<<"YES"<<'\n';
#define NO  cout<<"NO"<<'\n';
#define fr(i,z,n) for(int i = z;i <= n; i++)
#define ufr(i,n,z) for(int i = n;i >= z; i--)
typedef long long ll;
const ll maxn = 500, inf = 1e18;
const ll mod = 1100003;
using namespace std;
stack<char>sta;
vector<vector<int>>v(maxn, vector<int>(maxn));
int a[maxn];    //记录出入度
int cnt[2];
int m;
int n;
int gcd(int a, int b) {                    //辗转相除法求最大公约数
    return !b ? a : gcd(b, a % b);
}
void solve() {
    cin >> n;
    int ans = 0;
    fr(i, 1, n) {
        int x;
        cin >> x;
        ans = gcd(abs(x), ans);
    }
    cout << ans << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    int t = 1;
    //cin >> t;
    while (t--) {
        solve();
    }
}

Shichikuji and Power Grid
题意:给定n个城市,一个城市有电,必须在这个城市有发电站或者和一个有电的城市用电缆相连
在一个城市建造发电站的代价是c[i],i和j两个城市相连的代价是k[i] + k[j] 乘上两者的曼哈顿距离
求最小代价,建发电站个数及具体城市,连线个数及具体连线
思路:因为可以有建发电站这个选择,所以有个很好的思路就是,建一个源点0到各点边,边权为c[i],
这样跑一遍最小生成树即可,这里建源点到各点边借鉴了差分约束里的思想
总结:这题的构造很巧妙,可以记录一下
 

#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<cstring>
#include<math.h>
#include<map>
#include<vector>
#include<stack>
#define ms(x,y) memset(x,y,sizeof x);
#define YES cout<<"YES"<<'\n';
#define NO  cout<<"NO"<<'\n';
#define fr(i,z,n) for(int i = z;i <= n; i++)
#define ufr(i,n,z) for(int i = n;i >= z; i--)
#define int long long
typedef long long ll;
const ll maxn = 2e3+10, inf = 1e18;
const ll mod = 1e9 + 7;
using namespace std;
struct edge {
    int x, y, w;
    inline int operator < (const edge& p) const {
        return w < p.w;
    }
}e[maxn*maxn];
int x[maxn], y[maxn], c[maxn];
int k[maxn];
int f[maxn];
int d[maxn][2];
//bool cmp(edge a, edge b) {
//    return a.w < b.w;
//}
int find(int x) {
    return (x == f[x]) ? x : find(f[x]);
}
void solve() {
    int n;
    cin >> n;
    int cnt = 0;
    ll ans = 0;
    fr(i, 1, n) {
         cin>>x[i];
         cin>>y[i];
    }
    fr(i, 1, n) {
        int w;
       cin>> w;
        e[++cnt].x = 0;
        e[cnt].y = i;
        e[cnt].w = w;
    }
    fr(i, 1, n) {
        cin>>k[i];
    }
    fr(i, 1, n) {
        fr(j, i + 1, n) {
            e[++cnt].x = i;
            e[cnt].y = j;
            e[cnt].w = (k[i] + k[j]) * (abs(x[i] - x[j]) + abs(y[i] - y[j]));
        }
    }
    sort(e + 1, e + 1 + cnt);
    fr(i, 1, n) {
        f[i] = i;
    }
    int num=0;
    int num1 = 0;
    for (int i = 1; i <= cnt; i++) {
        int x = find(e[i].x);
        int y = find(e[i].y);
        if (x == y)
            continue;
        f[min(x, y)] = max(x, y);
        ans += e[i].w;
        //cout << ans << '\n';
        if (!e[i].x) {
            c[++num] = e[i].y;
        }
        else {
            d[++num1][0] = e[i].x;
            d[num1][1] = e[i].y;
        }
    }
    cout << ans << '\n';
    cout << num << '\n';
    fr(i, 1, num) {
        cout << c[i] << ' ';
    }
    cout << '\n';
    cout << num1 << '\n';
    fr(i, 1, num1) {
        cout << d[i][0] << ' ' << d[i][1] << '\n';
    }    
}
signed main() {
    ios::sync_with_stdio(false);
    int t = 1;
    //cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值