AOI第四题琵琶湖题解

去年就参加的这个比赛,拿了310分,最后这一题当时不会写并查集只拿了十分。学了并查集之后,就想要把它写出来。可是……经过几个月的几次编写,就在今天终于ac了!
附ac证
记录信息
评测状态 Accepted
题目 P1944 琵琶湖
递交时间 2015-10-17 21:16:21
代码语言 C++
评测机 VijosEx
消耗时间 10384 ms
消耗内存 13484 KiB
评测时间 2015-10-17 21:16:26
评测结果
编译成功

测试数据 #0: Accepted, time = 0 ms, mem = 13480 KiB, score = 5
测试数据 #1: Accepted, time = 15 ms, mem = 13480 KiB, score = 5
测试数据 #2: Accepted, time = 31 ms, mem = 13484 KiB, score = 5
测试数据 #3: Accepted, time = 15 ms, mem = 13480 KiB, score = 5
测试数据 #4: Accepted, time = 375 ms, mem = 13480 KiB, score = 5
测试数据 #5: Accepted, time = 656 ms, mem = 13480 KiB, score = 5
测试数据 #6: Accepted, time = 609 ms, mem = 13476 KiB, score = 5
测试数据 #7: Accepted, time = 390 ms, mem = 13476 KiB, score = 5
测试数据 #8: Accepted, time = 390 ms, mem = 13480 KiB, score = 5
测试数据 #9: Accepted, time = 406 ms, mem = 13476 KiB, score = 5
测试数据 #10: Accepted, time = 375 ms, mem = 13480 KiB, score = 5
测试数据 #11: Accepted, time = 609 ms, mem = 13480 KiB, score = 5
测试数据 #12: Accepted, time = 328 ms, mem = 13476 KiB, score = 5
测试数据 #13: Accepted, time = 468 ms, mem = 13476 KiB, score = 5
测试数据 #14: Accepted, time = 609 ms, mem = 13480 KiB, score = 5
测试数据 #15: Accepted, time = 953 ms, mem = 13484 KiB, score = 5
测试数据 #16: Accepted, time = 906 ms, mem = 13480 KiB, score = 5
测试数据 #17: Accepted, time = 1203 ms, mem = 13480 KiB, score = 5
测试数据 #18: Accepted, time = 1015 ms, mem = 13480 KiB, score = 5
测试数据 #19: Accepted, time = 1031 ms, mem = 13480 KiB, score = 5
Accepted, time = 10384 ms, mem = 13484 KiB, score = 100

这题总的来说是一个倒序的并查集思想,用一个邻接表存储每个结点,并且离散化到所对应的年份,然后每次添加结点,使用并查集确定每次的岛屿个数,然后进行计算

4 条评论

  • @ 2016-08-27 17:54:38

    膜拜神犇

  • @ 2015-10-17 21:36:50

    #include<iostream>
    #include<stdio.h>
    using namespace std;

    int dir[2][5]={{1,-1,0,0},{0,0,1,-1}};

    int n,m;
    int h[1005][1005];
    int father[1000005];
    typedef pair<int,int> nodexy;
    inline nodexy getxy(int t){
    return make_pair((t-1)/m+1,(t-1)%m+1);
    }
    inline int getrl(int x,int y){
    return (x-1)*m+y;
    }
    int T,t[100005];
    int ans[100005];
    int head[100005];
    int NEXT[1000005];

    int find(int x){
    if(father[x]==x) return x;
    father[x]=find(father[x]);
    return father[x];
    }

    int main(){
    cin>>n>>m;
    int i,j;
    for(i=1;i<=n;i++)
    for(j=1;j<=m;j++)
    { scanf("%d",&h[i][j]);}
    cin>>T;
    for(i=1;i<=T;i++)
    { scanf("%d",&t[i]);}
    int x,y,k;
    nodexy xy;
    for(i=1;i<=n*m;i++)
    { x=(i-1)/m+1; y=(i-1)%m+1;
    int bot=0,top=T,mid=T/2;
    while(bot<top)
    { if(t[mid]<h[x][y])
    { bot=mid;}
    else
    { top=mid-1;}
    mid=(bot+top+1)/2;
    }
    h[x][y]=mid;
    NEXT[i]=head[mid];
    head[mid]=i;
    }
    for(i=T;i>=1;i--)
    { k=head[i];
    ans[i]=ans[i+1];
    while(k)
    { ans[i]++;
    father[k]=k;
    k=NEXT[k];}
    k=head[i];
    int x1,y1,f0,f1;
    while(k)
    { xy=getxy(k);
    x=xy.first;
    y=xy.second;
    for(j=0;j<4;j++)
    { x1=x+dir[0][j];
    y1=y+dir[1][j];
    if(x1>=1&&x1<=n&&y1>=1&&y1<=m){
    f0=find(getrl(x,y));
    f1=find(getrl(x1,y1));
    if(f0!=f1&&f0&&f1){
    ans[i]--;
    father[f0]=f1;
    }}
    }
    k=NEXT[k];
    }
    }
    for(i=1;i<=T;i++)
    printf("%d ",ans[i]);
    return 0;
    }

    这是最后一遍的程序,注意判断,仔细一点做,这题还是不难的

  • @ 2015-10-17 21:34:12

    #include<algorithm>

    #include<deque>

    #include<functional>

    #include<iterator>

    #include<list>

    #include<map>

    #include<memory>

    #include<memory.h>

    #include<numeric>

    #include<queue>

    #include<set>

    #include<stack>

    #include<utility>

    #include<vector>

    #include<iostream>

    #include<stdlib.h>

    #include<string.h>

    #include<math.h>

    #include<stdio.h>

    #include<time.h>

    #include<conio.h>

    using namespace std;

    #ifdef WIN32
    typedef __int64 ll;
    #else
    typedef long long ll;
    #endif

    int acmp(const void *p,const void *q){
    return *(int )p-(int *)q;
    }

    int bcmp(const void *p,const void *q){
    return acmp(q,p);
    }

    inline int mx(int a,int b){return a>b?a:b;}

    inline int mn(int a,int b){return a<b?a:b;}

    inline int ab(int x){return x>0?x:-x;}

    const int SIZE=1005,SIZET=100005,dSIZE=1000005;

    int ye[SIZET];
    int h[SIZE][SIZE];
    int t,n,m;
    int ans[SIZET];
    int head[SIZET];
    int next[dSIZE];
    int run[2][5]={{1,0,-1,0},
    {0,1,0,-1}};
    int father[dSIZE];
    int l;
    //i j的编号s为(i-1)*m+j
    //i=(s-1)/m+1
    //j=(s-1)%m+1

    int find(int x){//路径压缩
    if(father[x]==x) return x;
    father[x]=find(father[x]);
    return father[x];
    }

    void init(){//读入数据
    scanf("%d%d",&n,&m);
    int i,j;l=n*m;
    for(i=1;i<=n;i++)
    { for(j=1;j<=m;j++)
    { scanf("%d",&h[i][j]);}
    }
    scanf("%d",&t);
    for(i=1;i<=t;i++)
    { scanf("%d",ye+i);}
    }

    void discretization(){//进行离散化
    int i,x,y;l=n*m;
    for(i=1;i<=l;i++)
    { x=(i-1)/m+1; y=(i-1)%m+1;
    int bot=0,top=t,mid=t/2;
    while(bot<top)
    { if(ye[mid]<h[x][y])
    { bot=mid;}
    else
    { top=mid-1;}
    mid=(bot+top+1)/2;
    }
    h[x][y]=mid;
    next[i]=head[mid];
    head[mid]=i;//构造邻接表
    father[i]=i;
    }
    //离散化检验
    /*
    for(i=1;i<=l;i++)
    { x=(i-1)/m+1; y=(i-1)%m+1;
    printf("%d ",h[x][y]);
    if(i%m==0)puts("");
    }
    putchar('\n');
    /
    //高度模板检验
    /

    for(i=0;i<=t;i++)
    { int k=head[i],l=0;
    while(k){printf("%d,%d ",(k-1)/m+1,(k-1)%m+1);
    k=next[k];l++;}
    if(l) putchar('\n');
    }
    putchar('\n');
    */
    }

    void add(int k,int f){//增加k位置,即将k四周的一圈连接起来
    int x=(k-1)/m+1, y=(k-1)%m+1 ,i ,xg ,yg ,fa ,fk;
    ans[f]++;
    for(i=0;i<4;i++)
    if(h[x+run[0][i]][y+run[1][i]]>f)//该位置有岛
    { xg=x+run[0][i];
    yg=y+run[1][i];
    fa=find((xg-1)*m+yg);
    fk=find(k);
    if(fa!=fk)//不在同一个子集中
    { father[fk]=fa; ans[f]--;}//合并为同一子集
    }
    }

    void cube_father(int P){
    printf("\n\n%d:\n",P);int x,y,i;
    for(i=1;i<=l;i++)
    { printf("%4d",find(father[i]));
    if(i%m==0)puts("");}
    puts("");
    }

    void basic_build(){//构造ans[t]和初始father
    int k=head[t];//浮于水上的第一个结点
    while(k)
    { add(k,t);
    k=next[k];}
    //father检验
    //cube_father(t);
    }

    void ans_build(){
    int i,k;
    for(i=t-1;i>=1;i--){
    k=head[i];
    ans[i]=ans[i+1];
    while(k)
    { add(k,i);
    k=next[k];
    //cube_father(i);
    //cout<<ans[i]<<endl;
    }
    //cube_father(i);
    }
    }

    void op(){
    int i;
    for(i=1;i<=t;i++)
    { printf("%d ",ans[i]);}
    }

    int main(){
    freopen("lakepiwa.in","r",stdin);
    freopen("lakepiwa.out","w",stdout);
    init();
    discretization();
    basic_build();
    ans_build();
    op();
    return 0;
    }

    这是第二次写的程序,相比之下肯定考虑的要成熟的多,也有了基本的结构模型,很遗憾还是错了,但是这其中的一些关于离散化的代码什么的,都检验无误,也给第三次的程序创造了条件。当然,写程序还是要多调试啊。

  • @ 2015-10-17 21:30:49

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<cstdlib>
    #include<algorithm>
    #include<ctime>
    #include<queue>
    #include<stack>
    using namespace std;

    int father[1000010];//f[i*n+j]表示第ij水位的父岛屿
    int f[1010][1010];//表示第ij的高度
    int L[100010];//表示年份
    int ans[100010];//表示第L[i]年的岛屿情况
    int n,m,l,now;
    int Lfirst[100010];//表示第L[i]水位以上的位置

    struct Edge{
    int p;//表示坐标
    int next;
    }edge[1000010];//表示高于L[i]水位的情况

    int find(int x){//路径压缩
    if(father[x]==x) return x;
    father[x]=find(father[x]);
    return father[x];
    }

    int match(int i){//将i位置的格子连接起来,返回区域数
    int x=i/m+1,y=i%m,s=1,j;//(i*m-m+j)+m
    if(y==0){y=m;x--;}
    int p[10]={find(i)},sa[10]={0};
    if(x+1<=n&&f[x+1][y]>L[now]){p[1]=find(x*m+y);}else{p[1]=p[0];}
    if(y+1<=m&&f[x][y+1]>L[now]){p[2]=find(x*m-m+y+1);}else{p[2]=p[0];}
    if(y-1&&f[x][y-1]>L[now+1]){p[3]=find(x*m+y-1-m);}else{p[3]=p[0];}
    if(x-1<=m&&f[x-1][y]>L[now+1]){p[4]=find(x*m-m*2+y);}else{p[4]=p[0];}
    for(j=1;j<=4;j++)
    if(find(p[j])!=p[0]){
    father[p[j]]=p[0];
    s--;
    }
    return s;
    }

    void init(){
    scanf("%d%d",&n,&m);
    int i,j,Fir,End,Now;
    for(i=1;i<=n;i++)
    for(j=1;j<=m;j++)
    scanf("%d",&f[i][j]);
    scanf("%d",&l);
    for(i=1;i<=l;i++)
    scanf("%d",&L[i]);
    for(i=1;i<=n;i++)
    for(j=1;j<=m;j++){
    Fir=0;End=l;
    Now=(Fir+End)/2;
    while(Fir<End){
    if(L[Now]<f[i][j]) Fir=Now+1;
    else End=Now;
    Now=(Fir+End)/2;
    }
    edge[i*m-m+j].next=Lfirst[Fir-1];
    edge[i*m-m+j].p=i*m-m+j;
    Lfirst[Fir-1]=i*m-m+j;
    father[i*m-m+j]=i*m-m+j;
    }
    }

    void work(int k){
    ans[now]=ans[now+1];
    if(Lfirst[k]==0){return;}
    k=Lfirst[k];
    while(k){
    ans[now]+=match(edge[k].p);
    k=edge[k].next;
    }
    }

    void workall(){
    for(now=l;now>0;now--)
    work(now);
    for(now=1;now<=l;now++)
    printf("%d ",ans[now]);
    }

    int main(){
    freopen("lilypond.in","r",stdin);
    freopen("lilypond.out","w",stdout);
    init();
    workall();
    return 0;
    }

    不知道这个格式是怎么回事,可能可读性比较差,先就暂时这样吧。这是我第一次写的程序,当时考虑的是自下往上,从右往左的顺序添加结点,只考虑和右边、下边的等高结点和所有的比自身高的结点来合并。虽然到现在还不清楚到底为什么错了,但是程序可以说可读性很差,也很乱,很容易干扰思想,最后这种算法以失败告终

  • 1

信息

ID
1944
难度
7
分类
(无)
标签
递交数
646
已通过
111
通过率
17%
被复制
5
上传者