POJ 2296 二分+2-sat

题目大意:

给定n个点,给每个点都安排一个相同的正方形,使这个点落在正方形的下底边的中间或者上底边的中间,并让这n个正方形不出现相互覆盖,可以共享同一条边,求

这个正方形最大的边长

这里明显看出n个点,每个点都只有在上底边和下底边两种选择,所以这里是2-sat解决

这里全都是整数,而因为点在正方形的中间,所以/2后会有小数

我这里初始将所有点都扩大两倍,那么答案必然扩大两倍,所以我们二分只考虑边长为偶数的情况即可,这样计算结果就不会出现小数了

最后将答案除以2便是

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <vector>
  4 #include <algorithm>
  5 using namespace std;
  6
  7 #define N 210
  8 int n , S[N] , x[N] , y[N] , k;
  9 vector<int> G[N];
 10 bool mark[N];
 11
 12 struct Rec{
 13     int x[4] , y[4];
 14     bool in(Rec a){
 15         if(a.x[0]>=x[1] || a.y[0]>=y[2] || a.x[1]<=x[0] || a.y[2]<=y[0]) return false;
 16         return true;
 17     }
 18 }a , b , c , d;
 19
 20 void init(int n)
 21 {
 22     memset(mark , 0 , sizeof(mark));
 23     for(int i=0 ; i<2*n ; i++) G[i].clear();
 24 }
 25
 26 bool dfs(int u)
 27 {
 28     if(mark[u]) return true;
 29     if(mark[u^1]) return false;
 30     mark[u] = true;
 31     S[k++] = u;
 32     for(int i=0 ; i<G[u].size() ; i++)
 33         if(!dfs(G[u][i])) return false;
 34     return true;
 35 }
 36
 37 bool solve(int n)
 38 {
 39     for(int i=0 ; i<2*n ; i+=2)
 40         if(!mark[i] && !mark[i^1]){
 41             k= 0;
 42             if(!dfs(i)){
 43                 while(k) mark[S[--k]] = false;
 44                 if(!dfs(i^1)) return false;
 45             }
 46         }
 47     return true;
 48 }
 49
 50 void add_clause(int a , int p , int b , int q)
 51 {
 52     int m=2*a+p;
 53     int n=2*b+q;
 54     //m,n互斥
 55     G[m].push_back(n^1);
 56     G[n].push_back(m^1);
 57 }
 58
 59 bool check(int m)
 60 {
 61     init(n);
 62     if(m&1) m--;
 63     for(int i=0 ; i<n ; i++){
 64         for(int j=i+1 ; j<n ; j++){
 65             //down , up 2*2 four case
 66             a.x[0] = x[i]-m/2 , a.y[0] = y[i];
 67             a.x[1] = x[i]+m/2 , a.y[1] = y[i];
 68             a.x[2] = a.x[1]   , a.y[2] = y[i]+m;
 69             a.x[3] = a.x[0]   , a.y[3] = y[i]+m;
 70
 71             b.x[0] = x[j]-m/2 , b.y[0] = y[j];
 72             b.x[1] = x[j]+m/2 , b.y[1] = y[j];
 73             b.x[2] = b.x[1]   , b.y[2] = y[j]+m;
 74             b.x[3] = b.x[0]   , b.y[3] = y[j]+m;
 75
 76             c.x[0] = x[i]-m/2 , c.y[0] = y[i]-m;
 77             c.x[1] = x[i]+m/2 , c.y[1] = y[i]-m;
 78             c.x[2] = c.x[1]   , c.y[2] = y[i];
 79             c.x[3] = c.x[0]   , c.y[3] = y[i];
 80
 81             d.x[0] = x[j]-m/2 , d.y[0] = y[j]-m;
 82             d.x[1] = x[j]+m/2 , d.y[1] = y[j]-m;
 83             d.x[2] = d.x[1]   , d.y[2] = y[j];
 84             d.x[3] = d.x[0]   , d.y[3] = y[j];
 85
 86             if(a.in(b)) add_clause(i , 0 , j , 0);
 87             if(a.in(d)) add_clause(i , 0 , j , 1);
 88             if(c.in(b)) add_clause(i , 1 , j , 0);
 89             if(c.in(d)) add_clause(i , 1 , j , 1);
 90         }
 91     }
 92     return solve(n);
 93 }
 94
 95 int main()
 96 {
 97   //  freopen("in.txt" , "r" , stdin);
 98     int T;
 99     scanf("%d" , &T);
100     while(T--)
101     {
102         scanf("%d" , &n);
103         for(int i=0 ; i<n ; i++){
104             scanf("%d%d" , &x[i] , &y[i]);
105             x[i]*=2 , y[i]*=2;
106         }
107         int l=0 , r=1e5 , ret=l;
108         while(l<=r){
109             int m = (l+r)>>1;
110             if(check(m)) ret=m , l=m+1;
111             else r=m-1;
112         }
113         printf("%d\n" , ret/2);
114     }
115     return 0;
116 }
时间: 2025-01-01 15:15:17

POJ 2296 二分+2-sat的相关文章

Map Labeler (poj 2296 二分+2-SAT)

Language: Default Map Labeler Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 1815   Accepted: 599 Description Map generation is a difficult task in cartography. A vital part of such task is automatic labeling of the cities in a map; whe

poj 2296 Map Labeler【二分+2-set】【经典】

题目:poj 2296 Map Labeler 题意:给出以下二维坐标点,然后让你往平面上放正方形,点必须落在正方形上面边的中点或者下面边的中点,正方形不能重叠,可以共用边.问最大正方形边的边长. 分析:这种最大化最小值或者最小化最大值的问题,我们都可以种二分+判断的方法来解,这个也不例外,关键是判断部分 我们现在二分枚举边长为diff,然后所有的点就变成了在正方形上面或者下面的问题了,二选一的问题很明显可以用2-set来判断的 这个题目的关键在于理解他们之间的二元关系并建图,下面我们来分析 首

POJ 2296 Map Labeler(2-sat)

POJ 2296 Map Labeler 题目链接 题意: 坐标轴上有N个点,要在每个点上贴一个正方形,这个正方形的横竖边分别和x,y轴平行,并且要使得点要么在正方形的上面那条边的中点,或者在下面那条边的中点,并且任意两个点的正方形都不重叠(可以重边).问正方形最大边长可以多少? 思路:显然的2-sat问题,注意判断两个矩形相交的地方,细节 代码: #include <cstdio> #include <cstring> #include <cstdlib> #incl

POJ 3525 二分+半平面交

Most Distant Point from the Sea Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 3812   Accepted: 1779   Special Judge Description The main land of Japan called Honshu is an island surrounded by the sea. In such an island, it is natural t

POJ 2296 Map Labeler(二分边长+2-sat判解)(经典题)

题意:给你n个点,要你在这n个点上放一个正方形,点只能在正方形的上边或下边的中点上,所有正方形大小一样, 不能重叠,求最大的正方形. 经典的题目,找约束关系要经过一些讨论. //320 KB 16 ms #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; const int N = 222; int n; struct n

POJ 1064 (二分)

题目链接: http://poj.org/problem?id=1064 题目大意:一堆棍子可以截取,问要求最后给出K根等长棍子,求每根棍子的最大长度.保留2位小数.如果小于0.01,则输出0.00 解题思路: 根据最长的棍子二分枚举切割长度. 这点很容易想到. 本题麻烦的地方在于小数的二分. 由于精度丢失问题,如果直接使用double进行二分,那么二分确定的最大长度必然是不精确的. 如:只有1根100.0的棍子,分成10段.如果使用double二分,那么就算把精度控制到0.0000001, 最

POJ 3122-Pie(二分+精度)

题目地址:POJ 3122 题意:给出n个pie的直径.有F+1个人,假设给每人分的大小同样(形状能够不同),每一个人能够分多少.要求是分出来的每一份必须出自同一个pi(既当pie大小为3.2,1,仅仅能分出两个大小为2的份,剩下两个要扔掉.) 思路:对每个人分的大小进行二分. 注意讲pi放在最后成.能够提高精度. Ps:wa了5发.不知道错在哪,然后把输出的%lf改成%f就A了,并不知道为什么...sad #include <stdio.h> #include <math.h>

POJ 3061 (二分+前缀和)

题目链接: http://poj.org/problem?id=3061 题目大意:找到最短的序列长度,使得序列元素和大于S. 解题思路: 两种思路. 一种是二分+前缀和.复杂度O(nlogn).有点慢. 二分枚举序列长度,如果可行,向左找小的,否则向右找大的. 前缀和预处理之后,可以O(1)内求和. #include "cstdio" #include "cstring" int sum[100005],n,s,a,T; bool check(int x) { i

poj 3122 (二分查找)

链接:poj 3122 题意:我生日派对时,准备了n个圆柱形的pie,半径比一定相同,但高都为1, 邀请了f个朋友,加上自己一共f+1人,需要将n个pie分给f+1个人 要求:每个人分得的pie尺寸要一样大, 并且同一个人所分的pie要是从同一个pie上得到的,n个pie分完后可以有剩余 求:每个人最多可以分多少 分析:因为同一个人所分的pie都来自同一个pie, 若每个人所分的最大体积为a,那么比a小的pie肯定得舍弃. 将每个人最多分得的pie看成半径为r的圆柱,则最大尺寸为PI*r*r*1