首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

UVA 11294 Wedding:2-SAT 模型解婚宴座位安排

     編輯:C++ 入門

UVA 11294 Wedding 題解

題目大意

n 對夫妻參加婚宴,所有人坐在長桌左側或右側。夫妻必須面對面坐(即一方在左、另一方在右)。新娘在左側。有 m 對人有矛盾,新娘不希望矛盾雙方同側。問是否存在滿足條件的安排,若有則輸出任一方案(輸出每對夫妻中坐在新娘同側的是 husband 還是 wife)。

2-SAT 建模

第 i 對夫妻定義布林變量 x_i:

  • x_i = true:妻子坐在新娘同側(左側)
  • x_i = false:丈夫坐在新娘同側(左側)

矛盾約束轉化為子句:

  • 兩丈夫矛盾:¬x_i ∨ ¬x_j(最多一個丈夫在左)
  • 兩妻子矛盾:x_i ∨ x_j(最多一個妻子在左)
  • 異性矛盾:¬x_i ∨ x_j 或 x_i ∨ ¬x_j(視誰在左而定)

2-SAT 標準解法 (Kosaraju / Tarjan SCC)

本程式碼使用經典的 DFS + 回溯標記法(非 SCC 縮點,但邏輯等價):

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;

const int MAXN = 1005;

struct TwoSAT {
    int n;
    vector<int> g[MAXN * 2];
    bool mark[MAXN * 2];
    int stk[MAXN * 2], top;

    bool dfs(int x) {
        if (mark[x ^ 1]) return false;
        if (mark[x]) return true;
        mark[x] = true;
        stk[top++] = x;
        for (int v : g[x])
            if (!dfs(v)) return false;
        return true;
    }

    void init(int n) {
        this->n = n;
        for (int i = 0; i < n * 2; ++i) g[i].clear();
        memset(mark, 0, sizeof(mark));
        mark[1] = true;  // 關鍵:x_0 (第0對) 的丈夫固定在右側?題目隱含條件
    }

    // xval: 0=husband, 1=wife
    void add_clause(int x, int xval, int y, int yval) {
        x = x * 2 + xval;
        y = y * 2 + yval;
        g[x ^ 1].push_back(y);  // ¬x → y
        g[y ^ 1].push_back(x);  // ¬y → x
    }

    bool solve() {
        for (int i = 0; i < n * 2; i += 2)
            if (!mark[i] && !mark[i + 1]) {
                top = 0;
                if (!dfs(i)) {
                    while (top) mark[stk[--top]] = false;
                    if (!dfs(i + 1)) return false;
                }
            }
        return true;
    }
};

TwoSAT solver;

int main() {
    int n, m;
    while (scanf("%d%d", &n, &m) == 2) {
        if (n == 0 && m == 0) break;
        solver.init(n);
        while (m--) {
            int u, v; char a, b;
            scanf("%d %c %d %c", &u, &a, &v, &b);
            int xval = (a == 'h') ? 0 : 1;
            int yval = (b == 'h') ? 0 : 1;
            solver.add_clause(u, xval, v, yval);
        }
        if (!solver.solve()) {
            puts("bad luck");
        } else {
            for (int i = 1; i < n; ++i) {
                printf("%d%c%c", i, solver.mark[2*i] ? 'h' : 'w', i == n-1 ? '\n' : ' ');
            }
        }
    }
    return 0;
}

關鍵點說明

  • 變量編號:2*i 代表 x_i = false (husband),2*i+1 代表 true (wife)
  • mark[1] = true 對應題目隱含條件(新娘新郎固定相對位置)
  • DFS 過程中衝突則回溯並嘗試相反賦值
  • 輸出格式:第 1 對到第 n-1 對,每對輸出編號與坐在新娘同側者

複雜度

  • 時間:O((n+m) * n) 最壞情況,實際極快
  • 空間:O(n+m)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved