凸包+二进制枚举——poj1873

注意剪枝一下,不然会t

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
#define N 20

typedef double db;
const db eps=1e-6;
const db pi=acos(-1);
int sign(db k){if (k>eps) return 1; else if (k<-eps) return -1; return 0;}
int cmp(db k1,db k2){return sign(k1-k2);}
int inmid(db k1,db k2,db k3){return sign(k1-k3)*sign(k2-k3)<=0;}// k3 在 [k1,k2] 内
struct point{
    db x,y;
    int v,l;
    point operator + (const point &k1) const{return (point){k1.x+x,k1.y+y};}
    point operator - (const point &k1) const{return (point){x-k1.x,y-k1.y};}
    point operator * (db k1) const{return (point){x*k1,y*k1};}
    point operator / (db k1) const{return (point){x/k1,y/k1};}
    bool operator < (const point k1) const{
        int a=cmp(x,k1.x);
        if (a==-1) return 1; else if (a==1) return 0; else return cmp(y,k1.y)==-1;
    }
    db abs(){return sqrt(x*x+y*y);}
    db abs2(){return x*x+y*y;}
    db dis(point k1){return ((*this)-k1).abs();}
};
int inmid(point k1,point k2,point k3){return inmid(k1.x,k2.x,k3.x)&&inmid(k1.y,k2.y,k3.y);}
db cross(point k1,point k2){return k1.x*k2.y-k1.y*k2.x;}
db dot(point k1,point k2){return k1.x*k2.x+k1.y*k2.y;}

vector<point> ConvexHull(vector<point> A, int flag){
    int n=A.size();vector<point> ans(n*2);
    sort(A.begin(),A.end());
    int now=-1;
    for(int i=0;i<A.size();i++){//下凸包
        while(now>0 && sign(cross(ans[now]-ans[now-1],A[i]-ans[now-1]))<flag)
            now--;
        ans[++now]=A[i];
    }
    int pre=now;
    for(int i=n-2;i>=0;i--){//上凸包
        while(now>pre && sign(cross(ans[now]-ans[now-1],A[i]-ans[now-1]))<flag)
            now--;
        ans[++now]=A[i];
    }
    if(n)ans.resize(now);
    return ans;
}

vector<point>p,v;
int n;
int Min,ans;

void solve(int S){
    v.clear();
    int sum=0,tot=0;//可用篱笆长度
    for(int i=0;i<n;i++){
        if((S>>i) & 1)v.push_back(p[i]);
        else tot+=p[i].l,sum+=p[i].v;
    }
    if(sum>=Min)return;
    vector<point>res=ConvexHull(v,1);

    db len=0;//求凸包周长
    for(int i=0;i<res.size();i++)
        len+=res[i].dis(res[(i+1)%v.size()]);
    if(sign(tot-len)>=0){
        if(sum<Min){
            Min=sum;ans=S;
        }
    }
}

int main(){
    int tt=0;
    while(cin>>n && n){
        Min=0x3f3f3f3f;
        tt++;
        p.clear();
        for(int i=1;i<=n;i++){
            point pp;
            scanf("%lf%lf%d%d",&pp.x,&pp.y,&pp.v,&pp.l);
            p.push_back(pp);
        }
        for(int S=1;S<(1<<n)-1;S++)
            solve(S);

        v.clear();
        db tot=0;
        for(int i=0;i<n;i++){
            if((ans>>i) & 1)v.push_back(p[i]);
            else tot+=p[i].l;
        }
        vector<point>res=ConvexHull(v,1);
        db len=0;//求凸包周长
        for(int i=0;i<res.size();i++)
            len+=res[i].dis(res[(i+1)%v.size()]); 

        if(tt>=2)puts("");
        printf("Forest %d\n",tt);
        printf("Cut these trees:");
        for(int i=0;i<n;i++)
            if(!(ans>>i & 1))cout<<" "<<i+1;
        puts("");
        printf("Extra wood: %.2f\n",tot-len);
    }
}

原文地址:https://www.cnblogs.com/zsben991126/p/12359437.html

时间: 2024-10-19 21:42:22

凸包+二进制枚举——poj1873的相关文章

poj1873(二进制枚举+求凸包周长)

题目链接:https://vjudge.net/problem/POJ-1873 题意:n个点(2<=n<=15),给出n个点的坐标(x,y).价值v.做篱笆时的长度l,求选择哪些点来做篱笆围住另一些点,使得选出的这些点的价值和最小,如果价值和相等要求个数最小. 思路: 看来这是WF的签到题吧.数据很小,直接二进制枚举 (1<<n),然后对未选出的点求凸包的周长,仅当选出点的长度l的和>=凸包周长时才更新答案. AC code: #include<cstdio>

UVa 818 切断圆环链(dfs+二进制枚举)

https://vjudge.net/problem/UVA-818 题意:有n个圆环,其中有一些已经扣在了一起.现在需要打开尽量少的圆环,使得所有圆环可以组成一条链,例如,有5个圆环,1-2,2-3,4-5,则需要打开一个圆环,如圆环4,然   后用它穿过圆环3和圆环5后再次闭合4,就可以形成一条链:1-2-3-4-5. 思路:从n个圆环中任意选择圆环,这就是枚举子集.所以这道题目可以用二进制枚举来做. 那么如何判断当前打开圆环是可行的呢?在去除打开的圆环后需要判断: ①:每个圆环的分支数都必

UVa818 Cutting Chains (二进制枚举)

链接:http://vjudge.net/problem/35523 分析:links记录初始圆环链的情况,然后二进制枚举编号为0~n-1的圆环哪个被打开了,一个圆环最多一个前驱和一个后继,所以judge判断如果有一个未打开的圆环同时和2个以上的未打开圆环相连就一定不能形成链,剪去.circle判断剩下的未打开圆环是否形成环,以及若未成环那么有多少条单链links_num,注意最后成链不用按顺序比如1->2->3...这样.然后判断一下打开的圆环数够不够把links_num条单链扣成一条链,若

POJ 2436 二进制枚举+位运算

题意:给出n头牛的得病的种类情况,一共有m种病,要求找出最多有K种病的牛的数目: 思路:二进制枚举(得病处为1,否则为0,比如得了2 1两种病,代号就是011(十进制就是3)),首先枚举出1的个数等于k的二进制数,然后跟所有的牛的代号一一比较,符合的           +1,找出其中和最大的:就是转换2进制麻烦,用位运算就好实现了,但是位运算不是很明白含义,明白了再补充: 知识点: 3 & 2 = 2,相同为1,不同为0, 011 & 010 = 010:(怎么利用的这个特点不明白):

1151 - Buy or Build(二进制枚举子集 + 并查集)

这题LRJ书上翻译的有问题,书上说两点之间的cost是两点的欧几里得距离,而题目要求两点的距离是两点欧几里得距离的平方. 其余就没什么好说的了,裸的并查集,需要注意的就是二进制枚举子集的问题. 二进制枚举子集: for(int i = 0 ; i < (1 << s) ; i++){ /*s是集合元素的个数*/ for(int j = 0 ; j < s ; j++){ if(!(s >> j) & 1) continue; else{ } } } 140548

UvaLive 6661 Equal Sum Sets 二进制枚举/DP

链接:http://vjudge.net/problem/viewProblem.action?id=49406 题意:根据给出的n,k,s求出n个数每个数都不大于k,和为s的序列(n个数每个都不同)的总情况数. 思路: 1.二进制枚举枚举出所有可能排列,并求和若和为s,则符合,否则不符合. 代码: #include<iostream> #include<set> #include<map> #include<queue> #include<cstri

wikioi 2144 分步二进制枚举+map记录

题目描述 Description 有n个砝码,现在要称一个质量为m的物体,请问最少需要挑出几个砝码来称? 注意一个砝码最多只能挑一次 输入描述 Input Description 第一行两个整数n和m,接下来n行每行一个整数表示每个砝码的重量. 输出描述 Output Description 输出选择的砝码的总数k,你的程序必须使得k尽量的小. 样例输入 Sample Input 3 10 5 9 1 样例输出 Sample Output 2 数据范围及提示 Data Size & Hint 1

CUGBACM_Summer_Tranning1 二进制枚举+模拟+离散化

整体感觉:这个组队赛收获还挺多的.自从期末考试以后已经有一个多月没有 做过组队赛了吧,可是这暑假第一次组队赛就找回了曾经的感觉.还挺不错的!继续努力!! 改进的地方:这次组队赛開始的时候题目比較难读懂,然后就感觉题目应该比較难吧,认为应该是区域赛难度的题目.尽管A题和B题自己都感觉能自己A的.可是可能对自己不太自信,所以让队友大帝敲了.要是当时自己敢敲一下的话,后续会更快的A掉吧. A题:二进制枚举 题目链接:https://icpcarchive.ecs.baylor.edu/external

二进制枚举

2017-08-03 11:34:36 writer:pprp 一个知识点,之前从来没有遇到,最近的集训中频繁用到这个,学习理解了 代码及分析如下: // 二进制枚举 // 用来解决例如下边这样的问题 // 给你一串数列,要你将其中所有可能出现的sum找出来,就是说每个数都有两个状态,选或者是不选, //那么就选择用二进制通过 0 1 来表示选还是不选 #include <iostream> using namespace std; int main() { int n; //这里n意思是需要