【Two-sat 练习】

日常鸣谢kuangbin博主
http://www.cnblogs.com/kuangbin/archive/2012/10/05/2712429.html
这是原文地址;

下面是我自己的理解:

hdu 1814
n个党派,每个党派有两个人 2*i-1、2i
12 34 56 78 ….
现要组建委员会,没个党派都需要派一个人
m对关系
a b 表示a和b相互讨厌
求一种字典序最小的组建方法,没有输出 NIE

个人觉得: 看代码就好了,代码十分之详细,这个确实可作为模板,写的很好


struct node{
    int to,next;
}edge[100010];
int head[100010],tot;
void init(){
    tot=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
bool vis[20005];  // 染色法

int sta[20010],top;
bool dfs(int u ){  //染色
    if(vis[u^1])  // 0^1=1   1^1=0    2^1=3 3^1=2   4^1=5 5^1=4
        return false;
    if(vis[u])
        return true;
    vis[u]=1;
    sta[top++]=u;
    for(int i=head[u];i!=-1;i=edge[i].next){   //标记u,并且去寻找与u相关的点
        if(!dfs(edge[i].to))
            return false;
    }
    return true;
}
bool twosat(int n){
    memset(vis,0,sizeof(vis));
    for(int i=0;i<2*n;i+=2){  // 0. 2. 3. 4. 5. 6.
        if(vis[i] || vis[i^1]) continue; //每一个党派只能选一个人
        top=0;
        if(!dfs(i)){   //对i 寻找路径找 不到合法的 解法。
            while(top)
                vis[sta[--top]]=false;
            if( !dfs(i^1) )  // 对i和i^1都没有合法路径,那么NIE
                return false;
        }
    }
    return true;
}
int main()
{
  //  freopen("1.txt","r",stdin);
    int n,m;
    int u,v;
    while(~scanf("%d %d",&n,&m)){
        init();
        while(m--){
            scanf("%d %d",&u,&v);
            u--,v--;
            addedge(u,v^1); //原关系:u,v   加边:u-v^1    v-u^1
            addedge(v,u^1); //所以 我们的边 :表示选ab 是同时选。 即u and v
//            printf("add:%d %d   %d %d\n",u,v^1,v,u^1);
        }
        if(twosat(n)){
            for(int i=0;i<2*n;i++)
                if(vis[i])
                    printf("%d\n",i+1);
        }
        else
            printf("NIE\n");
    }
    return 0;
}

hdu 3622
n对炸弹, 每次安置炸弹的时候只能选这一对中的一个,每个炸弹爆炸范围r都是一样,

求一个最大的r, 使所有爆炸范围互不相交

二分结果 判断可行性

n=100
可行性判断:
边的意义: 选a 则 选b

不知道kuangbin 大大博客上的解法为何要写那么复杂,还用了强连通分量,又是原图又是逆图,感觉稍显麻烦
我们可以和上一题差不多的做法,给 可行边建图,然后我们用dfs判断可行性。


#define eps 1e-7
struct node{
    int to,next;
}edge[100010];
int head[100010],tot;
bool vis[20005];  // 染色法
void init(){
    tot=0;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
}
void addedge(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}

int sta[20010],top;
bool dfs(int u ){  //染色
    if(vis[u^1])  // 0^1=1   1^1=0    2^1=3 3^1=2   4^1=5 5^1=4
        return false;
    if(vis[u])
        return true;
    vis[u]=1;
    sta[top++]=u;
    for(int i=head[u];i!=-1;i=edge[i].next){   //标记u,并且去寻找与u相关的点
        if(!dfs(edge[i].to))
            return false;
    }
    return true;
}
bool twosat(int n){
    for(int i=0;i<2*n;i+=2){  // 0. 2. 3. 4. 5. 6.
        if(vis[i] || vis[i^1]) continue; //每一对 点只能选一个炸弹
        top=0;
        if(!dfs(i)){   //对i 寻找路径找 不到合法的 解法。
            while(top)
                vis[sta[--top]]=false;
            if( !dfs(i^1) )  // 对i和i^1都没有合法路径,那么NIE
                return false;
        }
    }
    return true;
}
double x[105*2],y[105*2];
/*
 01  23  45  67  89

 */
double dist(double x,double y,double a,double b){
    return sqrt( (x-a)*(x-a) +  (y-b)*(y-b) );
}
int main()
{
    freopen("1.txt","r",stdin);
    int n,m;
    while (~scanf("%d", &n)) {
        for (int i = 0; i < n; i++) {
            scanf("%lf %lf %lf %lf ", &x[2 * i], &y[2 * i], &x[2 * i + 1],
                    &y[2 * i + 1]);
        }
        double l = 0, r = 1000000;
        for (int i = 1; i <= 100; i++) {
            double mid = (l + r) / 2;
            init();
            for (int i = 0; i < 2 * n - 2; i++) {  // 01.23..2n-2 2n-1
                int sta;
                if (i & 1)
                    sta = i + 1;
                else
                    sta = i + 2;
                for (int j = sta; j < 2 * n; j++) {
                    double dis = dist(x[i], y[i], x[j], y[j]);
                    if (dis - 2 * mid < eps) {
                        addedge(i, j ^ 1);
                        addedge(j, i ^ 1);  // u-v 是可行边
//                  printf("add : %d %d   ,%d %d\n",i,j^1,j,i^1);
                    }
                }
            }
            if (twosat(n))
                l = mid + 0.00001;
            else
                r = mid - 0.00001;
        }
        printf("%.2f\n", l);
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值