条件转化,2-sat BZOJ 1997

http://www.lydsy.com/JudgeOnline/problem.php?id=1997

1997: [Hnoi2010]Planar

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1810  Solved: 684
[Submit][Status][Discuss]

Description

Input

Output

Sample Input

2
6 9
1 4
1 5
1 6
2 4
2 5
2 6
3 4
3 5
3 6
1 4 2 5 3 6
5 5
1 2
2 3
3 4
4 5
5 1
1 2 3 4 5

Sample Output

NO
YES

 

思路:(from 出题人):http://www.cnblogs.com/jinkun113/p/4894499.html

如果只考虑简单的平面图判定,这个问题是非常不好做的。 
但是题目中有一个条件——这张图存在一条哈密顿回路。
我们把哈密顿回路在平面上画成一个圆。仔细观察一下。
每条边如果画在圆内都是一条弦,那如果弦在圆内相交怎么办?把另一条弦翻出去。能不能两条弦都翻出去呢?不能,因为如果两条边在圆内相交,那么它们在圆外也会相交。那我们是不是就相当于就多了一个条件:这两条边不能同时在一个域内。
所以,这张图中总共只有两个域,圆内和圆外。那么我们是不是就转化了模型:有若干个点和若干条边,你要给每个点黑白染色,使得每条边的两个端点颜色不同。直接DFS就可以了。还有个问题,边数是10^4,暴力连边会超时,但是平面图有一个定理:m<=3*n+6,那这个定理来剪枝就行了。
 
 
然后我们只要对输入的哈密顿环进行标号,然后这样就是一个圈,假定刚开始都是在圈内,然后我们只需要判断是否在圈内就行了。

 

好像我的代码因为用了map和edges这些东西,所以跑的速度比别人慢一个log= =,别人都是200ms左右,我是2000ms,人生第一次体验到了map所带来的绝望

#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
const int maxn = 1e5 + 5;
struct TwoSat{
    int n, c, s[maxn * 2];
    bool mark[maxn << 1];
    vector<int> G[maxn << 1];
 
    void init(int n){
        this->n = n;
        for (int i = 0; i < 2 * n; i++) G[i].clear();
        memset(mark, false, sizeof(mark));
    }
 
    void add_edge(int x, int xval, int y, int yval){
        x = x * 2 + xval, y = y * 2 + yval;
        G[x ^ 1].pb(y), G[y ^ 1].pb(x);
    }
 
    void display(){
        for (int i = 0; i < 2 * n; i++){
            printf("from = %d\n", i);
            for (int j = 0; j < G[i].size(); j++){
                printf("%d ", G[i][j]);
            }
            cout << endl;
        }
    }
 
    bool dfs(int x){
        if (mark[x ^ 1]) return false;
        if (mark[x]) return true;
        mark[x] = true;
        s[c++] = x;
        for (int i = 0; i < G[x].size(); i++){
            if (!dfs(G[x][i])) return false;
        }
        return true;
    }
 
    bool solve(){
        for (int i = 0; i < n * 2; i += 2){
            if (!mark[i] && !mark[i + 1]){
                c = 0;
                if (!dfs(i)){
                    while (c) mark[s[--c]] = false;
                    if (!dfs(i + 1)) return false;
                }
            }
        }
        return true;
    }
 
};
TwoSat tar;
int n, m;
map<int, int> id;
map<pair<int, int>, int> Point;
vector<pair<int, int> > edges;
 
bool test(int u1, int v1, int u2, int v2){
    if (u2 > u1 && u2 < v1 && v2 > v1) return true;
    if (u1 > u2 && u1 < v2 && v1 > v2) return true;
    return false;
}
 
int main(){
    int T; cin >> T;
    while (T--){
        scanf("%d%d", &n, &m);
        id.clear();
        edges.clear();
        for (int i = 0; i < m; i++){
            int u, v; scanf("%d%d", &u, &v);
            if (u > v) swap(u, v);
            edges.push_back(mk(u, v));
        }
        for (int i = 1; i <= n; i++){
            int u; scanf("%d", &u);
            id[u] = i;
        }
        if (m >= n * 3 + 6){
            puts("NO"); continue;
        }
        tar.init(m);
        for (int i = 0; i < m; i++){
            int x1 = edges[i].fi, y1 = edges[i].se;
            x1 = id[x1], y1 = id[y1];
            if (x1 > y1) swap(x1, y1);
 
            for (int j = i + 1; j < m; j++){
                int x2 = edges[j].fi, y2 = edges[j].se;
                x2 = id[x2], y2 = id[y2];
                if (x2 > y2) swap(x2, y2);
                //if (x1 == x2 || x1 == y2 || y1 == x2 || y1 == y2) continue;
                if (test(x1, y1, x2, y2)){///假定刚开始都是在里面的
                    tar.add_edge(i, 0, j, 0);
                    tar.add_edge(i, 1, j, 1);
                }
            }
        }
        //tar.display();
        if (tar.solve()) puts("YES");
        else puts("NO");
    }
    return 0;
}
View Code

 

posted @ 2017-03-19 22:06  知る奇迹に  阅读(236)  评论(0编辑  收藏  举报