【Two-sat 练习】

来源:互联网 发布:手机解压密码破解软件 编辑:程序博客网 时间:2024/05/17 07:31

日常鸣谢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-7struct 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 0
原创粉丝点击