NOIP2017模拟赛9

前言

可喜可贺,博主终于又AK了一次!尽管这套题比较简单。。

a星星

有n(1≤n≤400)个星星,每个星都有唯一的坐标(x,y)(1≤x,y≤N),请计算可以覆盖至少K(1≤K≤N)颗星的矩形的最小面积。矩形的边必须平行于X轴和Y轴,长度必须为正整数。星如果在矩形边上,也认为它是属于矩形内的。
(特别的,在本题中,星星是一个1*1的面,而不是一个点)
本题不由得让人想起“星星在你的窗口”

分析

说好的签到题呢?恕我(蒟蒻)直言,这道题有点难以签到啊。。
首先坐标很小(只有N),所以不用离散化。
然后就是考虑如何快速求一个矩形里面的星星的数量,显然可以用部分和来做。
最后就是暴力枚举以每一个点为起点,再枚举长,然后二分宽即可。
这种做法的时间复杂度略大,幸好评测机是香港记者。本题对于宽来说,可以使用单调队列优化时间复杂度。

程序

#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

int f[500][500],a[500][500],n,K,le,ri,mid,ans;

int main(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);

    scanf("%d%d",&n,&K);
    for (int i=1;i<=n;i++)  {
        int x,y;
        scanf("%d%d",&x,&y);
        a[x][y]++;
    }

    for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++)
    f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+a[i][j];

    ans=n*n;

    for (register int i=n;i>=1;--i)
    for (register int j=n;j>=1;--j)
    for (register int k=1;k<=i;k++)
    {
        if (f[i][j]-f[i-k][j]<K) continue;
        le=0;ri=ans/k;
        while (le+1<ri) {
            mid=(le+ri)>>1;
            if (f[i][j]-f[i-k][j]-f[i][j-mid]+f[i-k][j-mid]>=K) ri=mid;else le=mid;
        }
        if (f[i][j]-f[i-k][j]-f[i][j-ri]+f[i-k][j-ri]>=K) ans=ri*k;
    }
    printf("%d\n",ans);

    return 0;
}

小结

这一题我一开始没有想到可以用部分和来求区间内星星的数量,说明本蒟蒻的基础还是很不行。

b战争

X国和Y国是死对头,X国有N个炮台, Y国有M个基地和K个发电站,炮台、基地、发电站所在的位置的坐标都是整数。Y国的每个发电站能对距离其L以内(包括L)的基地供电。X国的每个炮台都可以发射无限次,每次发射可以击毁一个基地或者一个发电站,消耗的能量为两者距离的平方,这里的距离是欧几里德距离。X国决定要摧毁Y国的所有基地,我们说Y国的某个基地被摧毁的条件是:基地本身被直接摧毁或者对其供电的所有发电站被击。
问X国摧毁Y国所有基地需要消耗的总能量最小是多少。
1 <= N、M、K <= 50. 1<=L<=2000。所有的坐标的范围:[-500,500]。所有的炮台、基地、发电站不会重复。数据保证每个基地至少有一个发电站为其供电。

分析

在这题中,由于炮台可以发射无限次,所以一旦计算了每个基地或发电站与最近炮台的距离,炮台就没有用了。
或许是最近做的网络流的题有点多吧,看到这种要么做要么不做的问题不是第一个想到背包而是想到网络流。
这题用网络流就是正解了。模型是最小割模型。
构图:①由于每个基地最后都要无法运行,所以每个基地到T连一条流量为E(破坏这个基地所需要的能量)的边。如果这条边被割了,表示这个基地被炸了。
②由于发电站也能炸,所以S到每个发电站也连一条流量为E(破坏这个发电站所需要的能量)的边。如果这条边被割了,表示这个发电站被炸了。
③如果某个发电站A能向某个基地B提供能量,那么A向B连一条边,表示如果A被炸了,B就少了一个发电站为它提供能量。(如果B只由一个发电站提供能量,则B就相当于被炸了)

程序

#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

const int oo=10000000;
struct nd{
    int x,y;
}a[60],b[60],c[60];
struct node{
    int u,f;
    node *ne,*neg;
}E[100000],*he[120];
int dl[120],bfn[120],n,m,p,ans=0,S,T,cnt,L;

int dis(nd a,nd b) {
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}

void add(int x,int y,int f) {
    cnt++;
    E[cnt].u=y;E[cnt].f=f;E[cnt].ne=he[x];
    he[x]=&E[cnt];E[cnt].neg=&E[cnt+1];
    cnt++;
    E[cnt].u=x;E[cnt].f=0;E[cnt].ne=he[y];
    he[y]=&E[cnt];E[cnt].neg=&E[cnt-1];
}
void Init(){
    scanf("%d%d%d%d",&n,&m,&p,&L);
    for (int i=1;i<=n;i++)  scanf("%d",&a[i].x);
    for (int i=1;i<=n;i++)  scanf("%d",&a[i].y);
    for (int i=1;i<=m;i++)  scanf("%d",&b[i].x);
    for (int i=1;i<=m;i++)  scanf("%d",&b[i].y);
    for (int i=1;i<=p;i++)  scanf("%d",&c[i].x);
    for (int i=1;i<=p;i++)  scanf("%d",&c[i].y);
}
void pre(){
    S=0;T=110;
    for (int i=0;i<=T;i++) he[i]=NULL;
    for (int i=1;i<=m;i++)
    {
        int s=oo;
        for (int j=1;j<=n;j++)
        s=min(s,dis(b[i],a[j]));
        add(i+50,T,s);
    }
    for (int i=1;i<=p;i++)
    {
        int s=oo;
        for (int j=1;j<=n;j++)
        s=min(s,dis(c[i],a[j]));
        add(S,i,s);
    }
    for (int i=1;i<=p;i++)
    {
        for (int j=1;j<=m;j++)
        if (sqrt(dis(c[i],b[j]))<=L) add(i,j+50,oo);
    }
}

bool bfs(int S,int T) {
    int le=1,ri=1;dl[1]=S;
    memset(bfn,0,sizeof(bfn));
    bfn[S]=1;
    while (le<=ri) {
        int u=dl[le];
        for (node*ne=he[u];ne;ne=ne->ne) {
            if (bfn[ne->u]>0||ne->f==0) continue;
            ri++;dl[ri]=ne->u;bfn[ne->u]=bfn[u]+1;
        }
        le++;
    }
    return bfn[T]!=0;
}

int dfs(int k,int f) {
    if (k==T) return f;
    int ret=0;
    for (node*ne=he[k];ne&&f!=0;ne=ne->ne) {
        if (bfn[ne->u]<=bfn[k]||ne->f==0) continue;
        int t=dfs(ne->u,min(ne->f,f));
        ne->f-=t;ne->neg->f+=t;ret+=t;f-=t;
    }
    if (f!=0) bfn[k]=-1;
    return ret;
}

int main(){
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);

    Init();
    pre();
    for (;bfs(S,T);) {
        ans+=dfs(S,oo);
    }
    printf("%d\n",ans);

    return 0;
}

小结

比较经典的网络流,甚至不用拆点什么的乱七八糟的东西,理解题意发现炮台是没用的就好。

c染色树

一棵共含有X个结点的树,结点编号1至X,根结点编号是1。有Y种不同的颜色,颜色编号从1至Y。
现在给每个结点都染上一种颜色,整颗树染色后满足:
1、对于编号是i的颜色,整颗树当中,至少有一个结点被染成了颜色i。
2、根结点必须被染成1号颜色,而且整颗树当中,恰好要有Z个结点被染成1号颜色。

染色过程结束后,现在要计算染色的总代价,总代价等于每一条边的代价之和,那么怎么计算一条边的代价呢?
不妨设结点a与结点b之间有一条边,该边的权重是W。
1、如果结点a和结点b最终被染成不同的颜色,那么a与b之间的那条边的代价等于0。
2、如果结点a和结点b最终被染成相同的颜色,那么a与b之间的那条边的代价等于W。

现在的问题是:在满足上述要求的前提下,染色树最小的总代价是多少?如果无法完成染色的任务,输出-1。

分析

在这题中,可以很容易的分析到①只有颜色是1的节点与其他节点是不同的。换一种说法就是除了1以外的节点都是相同的。②颜色的数量是2的情况和其他情况是不同的。因为如果颜色数量只有2种,则每一个点的颜色要么是1要么是2,所以说对于一个点,假如ta的颜色是1,则与ta连接的点有两种情况,其中1是要产生权值的,2是不会产生权值,反之亦然。
但如果颜色的数量不止两种,那么对于一个点,假如ta的颜色是1,和颜色数量是2的是差不多的。但如果ta的颜色不是1(比如是2吧),则与ta连接的点有三种情况,2,1,和其他。可以贪心出与ta连接的点不可能是2,把它代替成“其他”会更优。
总结出这些性质,就可以开始解本题了。这题是最优值问题,又是一棵树,所以可以往DP方面去想。先想颜色数不是2的情况吧。
首先有一维阶段为节点,这是一定要的。题目的限制是1的数量,所以多记一维阶段是包括该节点所在的子树1的数量。由于当前节点的颜色会影响它父亲的取值,所以要再记录当前节点的颜色。这点的父亲节点的颜色也会影响到当前节点的颜色的取值,所以我分了f和g分别表示该点父亲不是1或该点父亲是1的最优解。由于这题节点间状态不止1维,所以我使用了大神们常用的左儿子右兄弟表示法来减少我的思维复杂度和程序复杂度。
接着就是找转移式了,具体可以看程序。
对于这种方法来说,颜色数是2的和颜色数是其他的稍改细节就可以了。

#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
using namespace std;

const int oo=40000000;
vector <int> e[320];
vector <int> v[320];
int f[320][320][2],g[320][320][2],ls[320],rs[320],w[320],n,c,s;

void dfs(int k,int fa) {
    int l=e[k].size(),lu;
    for (int i=0;i<l;i++)
    if (e[k][i]!=fa)
    {
        int u=e[k][i];w[u]=v[k][i];
        if (ls[k]==0) ls[k]=u;else rs[lu]=u;
        dfs(u,k);lu=u;
    }
}

void dfs2(int k) {
    if (k==0) return;
    dfs2(ls[k]);dfs2(rs[k]);
    f[k][0][0]=f[ls[k]][0][0]+f[rs[k]][0][0];
    g[k][0][0]=g[rs[k]][0][0]+f[ls[k]][0][0];
    f[k][0][1]=g[k][0][1]=oo;
    for (int i=0;i<=s;i++){
        f[k][i][0]=f[k][i][1]=g[k][i][0]=g[k][i][1]=oo;
        for (int j=0;j<i;j++)
        {
            f[k][i][0]=min(min(f[ls[k]][j][0],f[ls[k]][j][1])+min(f[rs[k]][i-j][0],f[rs[k]][i-j][1]),f[k][i][0]);//父亲节点不是1,当前节点也不是1.
            f[k][i][1]=min(min(g[ls[k]][j][0],g[ls[k]][j][1])+min(f[rs[k]][i-j-1][0],f[rs[k]][i-j-1][1]),f[k][i][1]);]);//父亲节点不是1,当前节点是1.
            g[k][i][0]=min(min(f[ls[k]][j][0],f[ls[k]][j][1])+min(g[rs[k]][i-j][0],g[rs[k]][i-j][1]),g[k][i][0]);//父亲节点是1,当前节点不是1.
            g[k][i][1]=min(min(g[ls[k]][j][0],g[ls[k]][j][1])+min(g[rs[k]][i-j-1][0],g[rs[k]][i-j-1][1])+w[k],g[k][i][1]);//这就是父亲节点和当前节点都是1的情况,记得要加上权值
        }
        f[k][i][0]=min(f[k][i][0],min(f[ls[k]][i][0],f[ls[k]][i][1])+min(f[rs[k]][0][0],f[rs[k]][0][1]));
        g[k][i][0]=min(g[k][i][0],min(f[ls[k]][i][0],f[ls[k]][i][1])+min(g[rs[k]][0][0],g[rs[k]][0][1]));
    }
}

void dfs3(int k) {
    if (k==0) return;
    dfs3(ls[k]);dfs3(rs[k]);
    f[k][0][0]=f[ls[k]][0][0]+f[rs[k]][0][0]+w[k];
    g[k][0][0]=g[rs[k]][0][0]+f[ls[k]][0][0];
    f[k][0][1]=g[k][0][1]=oo;
    for (int i=0;i<=s;i++){
        f[k][i][0]=f[k][i][1]=g[k][i][0]=g[k][i][1]=oo;
        for (int j=0;j<i;j++)
        {
            f[k][i][0]=min(min(f[ls[k]][j][0],f[ls[k]][j][1])+min(f[rs[k]][i-j][0],f[rs[k]][i-j][1])+w[k],f[k][i][0]);//父亲节点是2,当前节点也是2.
            f[k][i][1]=min(min(g[ls[k]][j][0],g[ls[k]][j][1])+min(f[rs[k]][i-j-1][0],f[rs[k]][i-j-1][1]),f[k][i][1]);
            g[k][i][0]=min(min(f[ls[k]][j][0],f[ls[k]][j][1])+min(g[rs[k]][i-j][0],g[rs[k]][i-j][1]),g[k][i][0]);
            g[k][i][1]=min(min(g[ls[k]][j][0],g[ls[k]][j][1])+min(g[rs[k]][i-j-1][0],g[rs[k]][i-j-1][1])+w[k],g[k][i][1]);
        }
        f[k][i][0]=min(f[k][i][0],min(f[ls[k]][i][0],f[ls[k]][i][1])+min(f[rs[k]][0][0],f[rs[k]][0][1])+w[k]);
        g[k][i][0]=min(g[k][i][0],min(f[ls[k]][i][0],f[ls[k]][i][1])+min(g[rs[k]][0][0],g[rs[k]][0][1]));
    }
}

int main(){
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);

    scanf("%d%d%d",&n,&c,&s);
    if (c+s>n+1) {printf("-1\n");return 0;}
    for (int i=1;i<n;i++) {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        e[x].push_back(y);e[y].push_back(x);
        v[x].push_back(z);v[y].push_back(z);
    }
        w[1]=0;
        for (int i=1;i<=s;i++) f[0][i][0]=f[0][i][1]=g[0][i][0]=g[0][i][1]=oo;
        f[0][0][1]=g[0][0][1]=oo;
        dfs(1,1);
        if (c!=2) dfs2(1); else dfs3(1);
        printf("%d",min(g[1][s][1],f[1][s][1]));

    return 0;
}

小结

这题是经典树形DP,脚踏实地的分析就不会错了。

总结

这套题不难,有5个人AK了,所以也没什么好骄傲的。。

  ——-梦美镇楼
在使用Python来安装geopandas包时,由于geopandas依赖于几个其他的Python库(如GDAL, Fiona, Pyproj, Shapely等),因此安装过程可能需要一些额外的步骤。以下是一个基本的安装指南,适用于大多数用户: 使用pip安装 确保Python和pip已安装: 首先,确保你的计算机上已安装了Python和pip。pip是Python的包管理工具,用于安装和管理Python包。 安装依赖库: 由于geopandas依赖于GDAL, Fiona, Pyproj, Shapely等库,你可能需要先安装这些库。通常,你可以通过pip直接安装这些库,但有时候可能需要从其他源下载预编译的二进制包(wheel文),特别是GDAL和Fiona,因为它们可能包含一些系统级的依赖。 bash pip install GDAL Fiona Pyproj Shapely 注意:在某些系统上,直接使用pip安装GDAL和Fiona可能会遇到题,因为它们需要编译一些C/C++代码。如果遇到题,你可以考虑使用conda(一个Python包、依赖和环境管理器)来安装这些库,或者从Unofficial Windows Binaries for Python Extension Packages这样的网站下载预编译的wheel文。 安装geopandas: 在安装了所有依赖库之后,你可以使用pip来安装geopandas。 bash pip install geopandas 使用conda安装 如果你正在使用conda作为你的Python包管理器,那么安装geopandas和它的依赖可能会更简单一些。 创建一个新的conda环境(可选,但推荐): bash conda create -n geoenv python=3.x anaconda conda activate geoenv 其中3.x是你希望使用的Python版本。 安装geopandas: 使用conda-forge频道来安装geopandas,因为它提供了许多地理空间相关的包。 bash conda install -c conda-forge geopandas 这命令会自动安装geopandas及其所有依赖。 注意事项 如果你在安装过程中遇到任何题,比如编译错误或依赖题,请检查你的Python版本和pip/conda的版本是否是最新的,或者尝试在不同的环境中安装。 某些库(如GDAL)可能需要额外的系统级依赖,如地理空间库(如PROJ和GEOS)。这些依赖可能需要单独安装,具体取决于你的操作系统。 如果你在Windows上遇到题,并且pip安装失败,尝试从Unofficial Windows Binaries for Python Extension Packages网站下载相应的wheel文,并使用pip进行安装。 脚本示例 虽然你的题主要是关于如何安装geopandas,但如果你想要一个Python脚本来重命名文夹下的文,在原始名字前面加上字符串"geopandas",以下是一个简单的示例: python import os # 指定文夹路径 folder_path = 'path/to/your/folder' # 遍历文夹中的文 for filename in os.listdir(folder_path): # 构造原始文路径 old_file_path = os.path.join(folder_path, filename) # 构造新文名 new_filename = 'geopandas_' + filename # 构造新文路径 new_file_path = os.path.join(folder_path, new_filename) # 重命名文 os.rename(old_file_path, new_file_path) print(f'Renamed "{filename}" to "{new_filename}"') 请确保将'path/to/your/folder'替换为你想要重命名文的实际文夹路径。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值