📂题解
🔖图论
2024-08-11 09:22阅读: 12评论: 0推荐: 0

一些做题记录

图论

P1993 小 K 的农场

复习差分约束QAQ。设农场 i 种植的作文有 xi 个单位,则题目中的三个条件就是:

{xaxb+cxaxb+cxa=xb

其中第一个不等式可以转化成

{xbxac

注意到最短路中对于任何节点 x ,都满足 disxdisy+wy,x ,所以我们只需要对于不等式一,从 ab 连一条长度为 c 的边,对于不等式二,从 ba 连一条长度为 c 的边,对于不等式三,因为 a=babba ,所以我们从 ab 连一条长度为 0 的边,再从 ba 连一条长度为 0 的边,最后跑一遍最短路即可。需要注意,有可能图中出现负环,判无解即可。

#include<bits/stdc++.h>
using namespace std;

#define N 6010
#define pii pair<int, int>

int n, m;
int vis[N], cnt[N], d[N];
vector<pii> e[N];

int spfa(int s){
  queue<pii> q;
  q.push({s, 0});
  memset(d, 0x3f, sizeof d);
  vis[s] = 1, d[s] = 0;
  while(!q.empty()){
    int u = q.front().first; q.pop();
    vis[u] = 0;
    for(auto v : e[u]){
      if(d[v.first] > d[u] + v.second){
        d[v.first] = d[u] + v.second;
        cnt[v.first] = cnt[u] + 1;
        if(cnt[v.first] >= 2 * n) return 0;
        if(!vis[v.first]){
          vis[v.first] = 1;
          q.push({v.first, d[v.first]});
        }
      }
    }
  }
  return 1;
}

signed main(){
//   freopen("sr.in", "r", stdin);
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n >> m;
  while(m--){
    int op, a, b, c;
    cin >> op >> a >> b;
    if(op == 1){
      cin >> c;
      e[a].push_back({b, -c});
    }
    else if(op == 2){
      cin >> c;
      e[b].push_back({a, c});
    }
    else{
      e[a].push_back({b, 0}), e[b].push_back({a, 0});
    }
  }
  for(int i = 1; i <= n; i ++){
    e[n + 1].push_back({i, 0});
  }
  if(spfa(n + 1)) cout << "Yes" << endl;
  else cout << "No" << endl;
  return 0;
}

P1195 口袋的天空

一眼最小生成树。但是题意仍然看了好久才看懂QAQ。就是给定 n 个点, m 条边,连成 k 个树的最小代价。容易想到 Kruskal,每一次建边将当前的连通块--,直到最后为 k 个即可。感觉Prim没什么应用场景啊

#include<bits/stdc++.h>
using namespace std;

#define N 1010
#define M 10010

int n, m, k;
int cnt, ans;
int fa[N];

struct EDGE{
  int a, b, w;
}e[M];

int find(int x){
  return fa[x] == x ? x : fa[x] = find(fa[x]);
}

int cmp(EDGE a, EDGE b){
  return a.w < b.w;
}

signed main(){
//   freopen("sr.in", "r", stdin);
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n >> m >> k;
  for(int i = 1; i <= m; i ++){
    int a, b, c;
    cin >> a >> b >> c;
    e[i] = {a, b, c};
  }
  for(int i = 1; i <= n; i ++){
    fa[i] = i;
  }
  cnt = n;
  sort(e + 1, e + m + 1, cmp);
  for(int i = 1; i <= m; i ++){
    int a = find(e[i].a), b = find(e[i].b);
    if(a == b) continue;
    cnt--;
    fa[a] = b;
    ans += e[i].w;
    if(cnt == k){
      cout << ans;
      return 0;
    }
  }
  cout << "No Answer";
  return 0;
}

P1550 [USACO08OCT] Watering Hole G

感觉很奇妙的一题。看似图中只有 n 个节点,实际上,完全可以将打井看作是向地下的某个隐藏节点连边。于是就加上这个节点跑最小生成树就好啦。

我才不会说我写Kruskal忘排序了呢

#include<bits/stdc++.h>
using namespace std;

#define N 310
#define M 100010

int n, m;
int cnt = 0;
int p[N];

struct EDGE{
  int a, b, w;
}e[M];

int find(int x){
  return p[x] == x ? x : p[x] = find(p[x]);
}

int cmp(EDGE a, EDGE b){
  return a.w < b.w;
}

void Kruskal(){
  sort(e + 1, e + cnt + 1, cmp);
  int ans = 0;
  for(int i = 1; i <= n + 1; i ++) p[i] = i;
  for(int i = 1; i <= cnt; i ++){
    int a = find(e[i].a), b = find(e[i].b);
    if(a == b) continue;
    ans += e[i].w;
    p[a] = b;
  }
  cout << ans;
}

signed main(){
  //freopen("sr.in", "r", stdin);
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n;
  for(int i = 1; i <= n; i ++){
    int w;
    cin >> w;
    e[++cnt] = {n + 1, i, w};
  }
  for(int i = 1; i <= n ;i ++){
    for(int j = 1; j <= n; j ++){
      int w;
      cin >> w;
      e[++cnt] = {i, j, w};
    }
  }
  Kruskal();
  return 0;
}

本文作者:星影流灿

本文链接:https://www.cnblogs.com/yduck/p/18353098

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   星影流灿  阅读(12)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起