【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
- 【Two-sat 练习】
- Poj 3678 (two-sat)
- hdu4751 two-sat
- 2sat 题目练习
- Poj 2723(two-sat)
- live archive 3713 two-sat
- SAT数学题练习两道
- SAT数学练习题目两个
- UVa 11294 Wedding (two SAT 输出解)
- hdu 4421 Bit Magic(two-SAT+思维)
- codeforces 468B Two Sets 2-sat
- CF -- 468B Two Set (2-SAT)
- 练习---two sum
- [打字练习]Day Two
- linux练习two
- 小练习:Two Sum
- SAT阅读练习(三):SAT Sentence Completion 3
- SAT数学题练习之概率部分
- Android View体系之View坐标系
- Girls' research
- 俄罗斯套娃问题(Java)
- html5操作本地数据库
- bzoj 3007: 拯救小云公主 最小生成树
- 【Two-sat 练习】
- linux中test命令详解
- Android无限广告轮播
- NYOJ-264 国王的魔镜
- NYOJ-ACM-小明的调查作业
- 做题技巧
- 并查集
- Android Fragment 从源码的角度去解析(上)
- 如何查看Android API文档