BZOJ3970 WF2013 G Map Tiles

想象一个框把这个多边形框住,如果知道这个框的左上角的确切位置就能知道答案。

可以发现,这个位置有如下4种情况:

1. 一个顶点在水平线上,一个顶点在竖直线上

2. 一个顶点在水平线上,一个网格点在多边形边上

3. 一个顶点在竖直线上,一个网格点在多边形边上

4. 两个网格点在多边形边上

这样理论的点数有 O(102n2) 个,但是点去重后实际数量非常少。

现在的问题是如何快速计算网格数

使用扫描线,对于一块长为 1 的,可以把里面区间扫一下之类的,拿个二进制数记录是否visit

#include<bits/stdc++.h>
#define maxm 500100
#define maxn 110
using namespace std;
typedef double ldb;
typedef long double Ldb;
const ldb eps=1e-7;
int inline dcmp(ldb x){
    return fabs(x)<eps?0:(x<0?-1:1);
}
struct point{
    ldb x,y;
    point(){}
    point(ldb x,ldb y):x(x),y(y){}
    point operator+(const point& p)const{return point(x+p.x,y+p.y);}
    point operator+=(const point& p){return *this=*this+p;}
    point operator-(const point& p)const{return point(x-p.x,y-p.y);}
    point operator-=(const point& p){return *this=*this-p;}
    point operator%(const point& p)const{return point(x-floor(x/p.x)*p.x,y-floor(y/p.y)*p.y);}
    point operator%=(const point& p){return *this=*this%p;}
    bool operator<(const point& p)const{return x==p.x?x<p.x:y<p.y;}
    bool operator!=(const point& p)const{return dcmp(x-p.x)||dcmp(y-p.y);}
    bool operator==(const point& p)const{return !dcmp(x-p.x)&&!dcmp(y-p.y);}
    void scan(){int a,b;scanf("%d%d",&a,&b);x=a,y=b;}
}wxhs[maxm],p[maxn],A[maxn];
int pd(ldb x,ldb L,ldb R){
//  printf("<%.3lf,%.3lf,%.3lf,%d>",x,L,R,dcmp(x-L)*dcmp(x-R)>0);
    return dcmp(x-L)*dcmp(x-R)>0;
}
int getwei(int l,int r){
    return ((1<<r-l+1)-1)<<l;
}
int getl(ldb a){
    return (int)floor(a+eps);
}
int getr(ldb a){
    if(dcmp(floor(a+eps)-a))return (int)floor(a+eps);
    else return (int)(floor(a+eps))-1;
}
typedef pair<ldb,ldb> par;
int n,w,ans,h,tp,ptr;
ldb B[110];
par C[110];
int cal(){
    int sum=0,ptr=0,tp=0;
    for(int i=1;i<=n;++i)
        B[++ptr]=A[i].x;
    for(int i=1;i<=11;++i)
        B[++ptr]=i;
    sort(B+1,B+ptr+1);
    int used=0,ptr2=0;
    for(int i=1;i<=ptr;++i)if(i==1||dcmp(B[ptr2]-B[i]))B[++ptr2]=B[i];
    ptr=ptr2;
    for(int i=1,nxt=1;i<=ptr;++i){
        for(int j=1;j<=n;++j)if(dcmp(A[j+1].x-A[j].x)){
            if(pd(B[i],A[j].x,A[j+1].x))continue;
            if(pd(B[i+1],A[j].x,A[j+1].x))continue;
            ldb y=(Ldb)(A[j+1].y-A[j].y)*(B[i]-A[j].x)/(Ldb)(A[j+1].x-A[j].x)+A[j].y;
            ldb ny=(Ldb)(A[j+1].y-A[j].y)*(B[i+1]-A[j].x)/(Ldb)(A[j+1].x-A[j].x)+A[j].y;
            C[++tp]=par(min(ny,y),max(ny,y));
        }
        sort(C+1,C+tp+1);
        if(tp%2!=0)exit(1);
        for(int i=1;i<=tp;i+=2)
            used|=getwei(getl(min(C[i].first,C[i].second)),getr(max(C[i+1].first,C[i+1].second)));
        if(!dcmp(B[i+1]-nxt)){
            sum+=__builtin_popcount(used);
            nxt++,used=0;
        }
        tp=0;

    }
    return sum;
}
int main(){
    scanf("%d%d%d",&n,&w,&h);
    for(int i=1;i<=n;++i)p[i].scan();
    p[n+1]=p[1];
    //case 1:one in ver line;one in hor line ->n^2
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            wxhs[++tp]=point(p[i].x,p[j].y);
    //case 2:one in a ver line;one grid vertex on an edge(not ver) ->n^2*m
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            if(dcmp(p[j].x-p[j+1].x))
                for(int k=-10;k<=10;++k){
                    ldb nx=k*w+p[i].x;
                    if(pd(nx,p[j].x,p[j+1].x))continue;
                    ldb y=(p[j].y-p[j+1].y)*(nx-p[j].x)/(p[j].x-p[j+1].x)+p[j].y;
                    wxhs[++tp]=point(nx,y);
                }
    //case 3:one in a hor line;one grid vertex on an edge(not hor) ->n^2*m
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            if(dcmp(p[j].y-p[j+1].y))
                for(int k=-10;k<=10;++k){
                    ldb ny=k*h+p[i].y;
                    if(pd(ny,p[j].y,p[j+1].y))continue;
                    ldb x=(p[j].x-p[j+1].x)*(ny-p[j].y)/(p[j].y-p[j+1].y)+p[j].x;
                    wxhs[++tp]=point(x,ny);
                }
    //case 4:both on an edge -> n^2m^2 solve equations
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            for(int k=-10;k<=10;++k)
                for(int l=-10;l<=10;++l){
                    ldb A1=p[i+1].y-p[i].y;
                    ldb B1=p[i].x-p[i+1].x;
                    ldb C1=-p[i+1].y*p[i].x+p[i+1].x*p[i].y;
                    ldb A2=p[j+1].y-p[j].y;
                    ldb B2=p[j].x-p[j+1].x;
                    ldb C2=-p[j+1].y*p[j].x+p[j+1].x*p[j].y;
                    if(!dcmp(A1*B2-A2*B1))continue;
                    ldb K=A1*B2-A2*B1;
                    ldb x=(B1*A2*k*w+B1*B2*l*h+B1*C2-B2*C1)/K;
                    ldb y=-(k*w*A1*A2+B2*A1*l*h+C2*A1-C1*A2)/K;
                    if(dcmp(A1*x+B1*y+C1))printf("[%.7lf]",A1*x+B1*y+C1),exit(1);
                    if(dcmp(A2*(x+k*w)+B2*(y+l*h)+C2))exit(2);
                    if(pd(x,p[i].x,p[i+1].x))continue;
                    if(pd(x+k*w,p[j].x,p[j+1].x))continue;
                    wxhs[++tp]=point(x,y);
                }
    for(int i=1;i<=tp;++i)
        wxhs[i]%=point(w,h);
    sort(wxhs+1,wxhs+tp+1);
    int ans=1<<30,tp2=0;
    for(int i=1;i<=tp;++i)if(i==1||(wxhs[tp2]!=wxhs[i]))
        wxhs[++tp2]=wxhs[i];
    tp=tp2;
    for(int i=1;i<=tp;++i){
        ldb x=wxhs[i].x,y=wxhs[i].y;
        ldb dx=1e20,dy=1e20;
        for(int j=1;j<=n+1;++j){
            A[j].x=(p[j].x-x)/w;
            A[j].y=(p[j].y-y)/h;
            dx=min(dx,floor(A[j].x+eps));
            dy=min(dy,floor(A[j].y+eps));
        }
        for(int j=1;j<=n+1;++j)
            A[j].x-=dx,A[j].y-=dy; 
        ans=min(ans,cal());
    }
    printf("%d",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 树形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yfzcsc

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值