[北京省选集训2019]图的难题 题解

传送门

题意:给一张无向图,将其中的边染成黑色或白色,使得不存在同色环。问是否存在可行的染色方案。

观察可知一个结论:图 G = ( V , E ) G=(V,E) G=(V,E)的可行的染色方案存在,当且仅当对于任意子图 G ′ = ( V ′ , E ′ ) G'=(V',E') G=(V,E)都有 ∣ E ′ ∣ ≤ 2 ∣ V ′ ∣ − 2 |E'|\leq2|V'|-2 E2V2,即 ∣ E ′ ∣ − 2 ∣ V ′ ∣ ≤ − 2 |E'|-2|V'|\leq-2 E2V2
如果我们定义一张子图的权值 Q ( G ′ ) = ∣ E ′ ∣ − 2 ∣ V ′ ∣ Q(G')=|E'|-2|V'| Q(G)=E2V,那么只要 Q ( G ′ ) max ⁡ ≤ − 2 Q(G')_{\max}\leq-2 Q(G)max2。比较显然的最大权闭合子图问题:把边看成点,点权为 1 1 1;点的点权为 − 2 -2 2。那么我们从源点向代表边的点连边,容量是 1 1 1;从代表点的点向汇点连边,容量是 2 2 2;从边对应的点向其两个端点对应的点连边,容量是 + ∞ +\infty +表示割不掉;跑最大流,那么 Q max ⁡ = ∣ E ∣ − m a x f l o w Q_{\max}=|E|-\mathrm{maxflow} Qmax=Emaxflow
但是这样会有问题:空子图的权值 Q ( ∅ ) = 0 > − 2 Q(\empty)=0>-2 Q()=0>2,所以任何一张图都不合法。我们必须让我们的最大流算法不把空子图算进去。做法很简单,我们强制选一个点,那么从这个点对应的点到汇点的边就不连了,最后 Q max ⁡ = ∣ E ∣ − 2 − m a x f l o w Q_{\max}=|E|-2-\mathrm{maxflow} Qmax=E2maxflow。那么强制选哪个点呢?枚举即可。

#include <cctype>
#include <cstdio>
#include <climits>
#include <algorithm>

template <typename T> inline void read(T& x) {
    int f = 0, c = getchar(); x = 0;
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
    if (f) x = -x;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
    read(x); read(args...); 
}
template <typename T> void write(T x) {
    if (x < 0) x = -x, putchar('-');
    if (x > 9) write(x / 10);
    putchar(x % 10 + 48);
}
template <typename T> inline void writeln(T x) { write(x); puts(""); }
template <typename T> inline bool chkmin(T& x, const T& y) { return y < x ? (x = y, true) : false; }
template <typename T> inline bool chkmax(T& x, const T& y) { return x < y ? (x = y, true) : false; }

const int maxv = 1607, maxe = 1e5, inf = INT_MAX;

int v[maxe << 1], cap[maxe << 1], flow[maxe << 1], next[maxe << 1], tot = -1;
int head[maxv], curr[maxv], q[maxv], height[maxv];
int s, t, V;

inline void ae(int x, int y, int c) {
    v[++tot] = y; cap[tot] = c; flow[tot] = 0; next[tot] = head[x]; head[x] = tot;
    v[++tot] = x; cap[tot] = 0; flow[tot] = 0; next[tot] = head[y]; head[y] = tot;
}
inline bool bfs() {
    for (int i = 1; i <= V; ++i) height[i] = 0;
    int l = 0, r = 1; height[q[1] = s] = 1;
    while (l < r) {
        int x = q[++l];
        for (int i = head[x]; ~i; i = next[i])
            if (cap[i] > flow[i] && !height[v[i]])
                height[q[++r] = v[i]] = height[x] + 1;
    }
    return height[t];
}
int dfs(int x, int cf) {
    if (x == t || !cf) return cf;
    int getf = 0;
    for (int i = curr[x]; ~i; i = next[i])
        if (cap[i] > flow[i] && height[v[i]] == height[x] + 1) {
            int nf = dfs(v[i], std::min(cf, cap[i] - flow[i]));
            if (nf) {
                flow[i] += nf; flow[i ^ 1] -= nf; getf += nf;
                if (!(cf -= nf)) break;
            }
        }
    return getf;
}
inline int maxflow() {
    int ans = 0;
    while (bfs()) {
        for (int i = 1; i <= V; ++i)
            curr[i] = head[i];
        ans += dfs(s, inf);
    }
    return ans;
}

struct Edge {
    int u, v;
    Edge(int x, int y) : u(x), v(y) {}
    Edge() : u(0), v(0) {}
};
Edge e[1007];
int T, n, m;

inline bool solve() {
    int ans = -inf;
    s = n + m + 1;
    t = V = s + 1;
    for (int u = 1; u <= n; ++u) {
        tot = -1;
        for (int i = 1; i <= V; i += 4) {
            head[i] = -1;
            head[i + 1] = -1;
            head[i + 2] = -1;
            head[i + 3] = -1;
        }
        for (int i = 1; i <= m; ++i) {
            int x = e[i].u, y = e[i].v;
            ae(s, i, 1);
            ae(i, x + m, inf);
            ae(i, y + m, inf);
        }
        for (int i = 1; i <= n; ++i)
            if (i != u) ae(i + m, t, 2);
        chkmax(ans, m - 2 - maxflow());
    }
    return ans <= -2;
}

int main() {
    read(T);
    while (T--) {
        read(n, m);
        for (int i = 1; i <= m; ++i) read(e[i].u, e[i].v);
        puts(solve() ? "Yes" : "No");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对不起,由于我是一个文本交互的模型,我无法提供像内容或直接链接到具体的题解或解决方案。但是,我可以帮你理解CSP-J2019公交换乘问题的基本概念和解决策略,这通常涉及到数据结构、论以及算法设计。 CSP-J2019中的公交换乘问题可能是一个典型的旅行商问题(Traveling Salesman Problem, TSP)变种,或者是寻找最优路径的问题,其中涉及到公交网络中不同站点之间的最短路径或最少换乘次数。解决此类问题通常需要使用动态规划、贪心算法或者一些启发式搜索算法,比如A*搜索或Dijkstra算法。 如果你需要了解题目的基本思路,可能会这样操作: 1. 建立一个,节点代表公交站点,边代表两个站点之间的路线及其长度或换乘次数。 2. 对于每个节点,计算从起点到所有其他节点的最短路径,形成一个邻接矩阵或邻接表。 3. 使用动态规划方法,例如记忆化搜索,尝试所有可能的路径,每次选择当前未访问节点中距离最近的一个,直到遍历完所有节点并回到起点,记录下总的距离或换乘次数。 4. 为了优化,可以考虑使用启发式搜索策略,如用估算的总距离作为启发信息,优先探索看起来更优的路径。 如果你对具体解法有疑问,或者想了解某个步骤的详细操作,请告诉我,我会尽力解释。至于详细的题解,建议你查阅相关的代码库、论坛帖子或在线教程,它们通常会有文字描述和步骤示例。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值