homework-02

最大子数组和问题的扩展

综述

这次作业是在第一次作业基础上进行的扩展。需要完成的任务有如下几个:

  1. 以命令行参数的形式传入输入文件名等控制参数,并对输入文件的正确性进行检验,保证程序不会崩溃。
  2. 处理一维和二维子矩阵的情况
  3. 处理数组左右边界相连或上下边界相连的情况,以-v表示左右相连,-h表示上下相连。这两个参数可以同时出现。
  4. 将最大子矩阵的“矩阵”条件降低为“连通子块”,上下或左右相邻视为连通。以-a表示。
  5. 考虑-a与-h和-v参数共存的情况。

分析

参数传入与输入检测

程序用c语言编写,所以参数传入靠main函数的argc和argv参数实现。

我首先考虑了参数个数。由于-a,-h,-v最多各出现一次,加上输入文件名最多4个参数,算上程序名,则argc>5则不合法,同时argc<2也不合法。

然后再判断第2至argc-1个参数,判断他们是否是-a,-h或-v中的一种,并且是否仅出现一次。

之后再判断最后一个参数,即输入文件名。检查文件是否存在且成功打开。

成功打开文件后可以开始检测输入文件内容是否合法。以字符串形式读入前两行,用strtol函数进行整数转换,并检查是否有多余字符。读入n,m后用类似方法读入后面的n行。同时还要检查n,m和数组元素的范围。

一维与二维情况+上下左右相连

一般的一维与二维最大子矩阵已经在homework-01中解决,现在考虑如果加入-v应该怎么解决。 一维的情况可以用二维的方法解决,所以我们只考虑二维。

既然左右相连,那么我们可以将数组复制一下连到原数组的右边,在这个新数组上应用二维的方法,应该可以解决。 但考虑到这样做得到的结果矩阵的宽度可能会超出m(因为新数组宽度是2m),所以我们应当限制矩阵的宽度。

回忆一下二维做法:先枚举矩阵的上下边界,再用一维的做法从左到右 ,算出以每一列结尾的矩阵的最大情况,即:

f[i]=sum[i]-Min{sum[j]|0<=j<=i-1}

这里Min{sum[j]|0<=j<=i-1}是i之前的sum的最小值,为了避免j与i相差超过m(也就意味着以i列为结尾的矩阵宽度超过m), 我们需要对这个式子做一下修改:

Min{sum[j]| Max(0,i-m)<=j<=i-1}

但是这个值无法像以前一样维护了。 以前的Min是从0到i-1的最小值,现在的Min是Max(0,i-m)到i-1的最小值。注意这个区间是不断向右移动的,区间的左端点单调递增, 可以用单调队列来维护。总的维护代价是O(n)的,每次查询是O(1)的,可以保持总的复杂度不变,仍是O(n^3)。这样就较好的解决了左右相连的情况

上下相连的情况类似,要将数组复制一下连到原数组的下面。这时同样需要限制矩阵的高度不能超过n。 方法比上面要简单,因为我们是先枚举的矩阵上下边界,只要在枚举的时候加入限制就可以了。

如果是上下左右都相连的情况,只需要将数组复制3份分别连到原数组的右,下和右下方,构成 一个2n*2m的新数组,并同时限制矩阵高度和宽度即可。

最大连通子块和

这一问我想了很久,最终也没能想出一个完美的做法。如果所给数组其中一维小于15,那么可以用基于连通性的状态压缩dp来解决。 但无奈最终老师要求32*32,这几乎否定了任何指数级的算法。而多项式算法更是难以得到最优解。我最终选择了一个较优解法。

首先,如果数组中全是负数,那么取一个最大的数,结果就是他(我们假定要求结果不能为空)。

如果数组中有非负数,我们将这些数标记为选中的,然后按连通性将他们连成块,得到一些仅由非负数组成的块。如果只有一个块,那么很显然,他就是最终结果。 如果有多个块,我们考虑如何将这些块连起来,得到和更大的块。

这是一个很复杂的问题,因为有可能通过一个奇怪的形状将几个块同时连起来,而这个形状是难以用数据去描述的。 退而求其次,我们考虑仅用简单路将块连起来。这个方法类似最小生成树。

注意现在剩下的数组元素都是负数,我们如果要连接两个块,又希望结果尽可能大,那么肯定要选择一条和最大的路。 可以将剩下的元素求相反数然后做最短路,看成求花费最少的路。注意这个路是块到块之间的,是指任何能连通两个块的路中 最短的。我们对这条路还有一个要求,即这条路的花费不能超过他连接的两个块中任意一个块的值,这是显然的,因为如果超过了, 那么新产生的块的值会比原来两个块中的一个小,不如不连。

如果存在符合条件的最短路,选中这条路,更新分块情况,再找新的路,直到不存在为止。 这时在最终的块中选择一个最大的作为结果。

这个方法存在反例,但不太容易构造,应该算是一个较优解,并且有优化的余地。

结果

程序结果与代码覆盖率见GitHub

代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXN 2000000000
#define ARRAY 40
#define BLOCK 50
#define SURROUND 150
FILE *fin,*fout;
const long int dx[5]={0,-1,0,1,0};
const long int dy[5]={0,0,1,0,-1};
void ArgCheck(long int argc, char * argv[],long int count[]);
void Error(char *s);
void Input(long int *n,long int *m,long int a[][ARRAY]);
long int GetInt(char *s,char **end);
void Work(long int n,long int m,long int a[][ARRAY],long int count[]);
long int Rectangle(long int n,long int m,long int LimN,long int LimM,long int a[][ARRAY]);
long int MonoList(long int x[],long int len,long int Lim);
long int InterConnect(long int n,long int m,long int a[][ARRAY]);
long int Partition(long int n,long int m,long int a[][ARRAY],long int fixed[][ARRAY],long int part[][ARRAY],long int value[]);
long int DFS(long int i,long int j,long int num,long int n,long int m,long int a[][ARRAY],long int fixed[][ARRAY],long int part[][ARRAY]);
void SPFA(long int n,long int m,long int bx,long int by,long int a[][ARRAY],long int fixed[][ARRAY],long int dis[][ARRAY],long int prex[][ARRAY],long int prey[][ARRAY]);
void GetContour(long int n,long int m,long int fixed[][ARRAY],long int part[][ARRAY],long int conx[][SURROUND],long int cony[][SURROUND],long int ncon[BLOCK]);
void TestOut(long int a[][ARRAY],long int l1,long int l2);
long int main(long int argc, char * argv[])
{
    long int n,m,a[ARRAY][ARRAY],count[3];
    FILE *fout=fopen("output.txt","w");
    ArgCheck(argc,argv,count);
    Input(&n,&m,a);
    Work(n,m,a,count);
    return 0;
}
void ArgCheck(long int argc, char * argv[],long int count[])
{
    if(argc>5)
        Error("too many arguments");
    if(argc<2)
        Error("too few arguments");
    long int i;

    count[0]=count[1]=count[2]=0;
    for(i=1;i<argc-1;i++)
    {
        if(argv[i][0]!='-' || (argv[i][1]!='a' && argv[i][1]!='h' && argv[i][1]!='v') || argv[i][2]!='\0')
            Error("invalid argument");
        else if(argv[i][1]=='a')
            count[0]++;
        else if(argv[i][1]=='h')
            count[1]++;
        else if(argv[i][1]=='v')
            count[2]++;
    }

    if(count[0]>1 || count[1]>1 || count[2]>1)
        Error("repeated arguments");

    fin=fopen(argv[argc-1],"r");
    if(fin==NULL)
        Error("no such file");
}
void Error(char *s)
{
    printf("%s\n",s);
    exit(1);
}
void Input(long int *n,long int *m,long int a[][ARRAY])
{
    long int i,j;
    char s[500],*ptr,*endptr;

    fscanf(fin,"%s",s);
    ptr=s;
    *n=GetInt(ptr,&endptr);
    if(endptr[0]!=',' || endptr[1]!='\0')
        Error("invalid characters appears");
    if(*n>32)
        Error("n is too big");
    if(*n<1)
        Error("n must be positive");
    fscanf(fin,"%s",s);
    ptr=s;
    *m=GetInt(ptr,&endptr);
    if(endptr[0]!=',' || endptr[1]!='\0')
        Error("invalid characters appears");
    if(*m>32)
        Error("m is too big");
    if(*m<1)
        Error("m must be positive");
    for(i=1;i<=*n;i++)
    {
        fscanf(fin,"%s",s);
        ptr=s;
        for(j=1;j<=*m;j++)
        {
            endptr=NULL;
            a[i][j]=GetInt(ptr,&endptr);
            if(endptr[0]!=',' && endptr[0]!='\0')
                Error("invalid characters appears");
            if(a[i][j]>2000000)
                Error("element is too big");
            if(a[i][j]<-2000000)
                Error("element is too small");

            ptr=endptr+1;
        }
        if(endptr[0]!='\0')
            Error("invalid end of line");
    }
}
long int GetInt(char *s,char **end)
{
    long int x;
    x=strtol(s,end,10);
    if(*end==s)
        Error("not long integer");
    if(*end[0]!=',' && *end[0]!='\0')
        Error("invalid character appears");
    return x;
}
void Work(long int n,long int m,long int a[][ARRAY],long int count[])
{
    long int ans,i,j;
    fout=fopen("output.txt","w");
    if(count[0]==0 && count[1]==0 && count[2]==0)
    {
        ans=Rectangle(n,m,n,m,a);
        fprintf(fout,"%d\n",ans);
    }
    else if(count[0]==0 && count[1]==1 && count[2]==0)
    {
        for(i=1;i<=n;i++)
            memmove(a[i]+m+1,a[i]+1,sizeof(long int)*m);
        ans=Rectangle(n,2*m,n,m,a);
        fprintf(fout,"%d\n",ans);
    }
    else if(count[0]==0 && count[1]==0 && count[2]==1)
    {
        for(i=1;i<=n;i++)
            memmove(a[i+n],a[i],sizeof(long int)*m);
        ans=Rectangle(2*n,m,n,m,a);
        fprintf(fout,"%d\n",ans);
    }
    else if(count[0]==0 && count[1]==1 && count[2]==1)
    {
        for(i=1;i<=n;i++)
        {
            memmove(a[i]+m+1,a[i]+1,sizeof(long int)*m);
            memmove(a[i+n],a[i],sizeof(long int)*m);
            memmove(a[i+n]+m+1,a[i]+1,sizeof(long int)*m);
        }
        ans=Rectangle(2*n,2*m,n,m,a);
        fprintf(fout,"%d\n",ans);

    }
    else if(count[0]==1)
    {
        ans=InterConnect(n,m,a);
        fprintf(fout,"%d\n",ans);
    }
}
long int Rectangle(long int n,long int m,long int LimN,long int LimM,long int a[][ARRAY])
{
    long int sum[2*ARRAY][2*ARRAY],sum1[2*ARRAY],minN,ans,temp,i,j,k;
    for(i=0;i<=n;i++)
        memset(sum[i],0,sizeof(long int)*(m+1));

    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
    ans=-MAXN;
    for(i=1;i<=n;i++)
    {
        if(i+LimN-1<n)
            minN=i+LimN-1;
        else
            minN=n;

        for(j=i;j<=minN;j++)
        {
            for(k=0;k<=m;k++)
                sum1[k]=sum[j][k]-sum[i-1][k];

            temp=MonoList(sum1,m,LimM);
            if(temp>ans)
                ans=temp;
        }

    }
    return ans;
}
long int MonoList(long int x[],long int len,long int Lim)
{
    long int head,tail,ans,temp,i;
    long int list[2*ARRAY];

    list[1]=0;
    head=1;
    tail=1;
    if(x[list[tail]]>=x[1])
        list[tail]=1;
    else
        list[++tail]=1;

    ans=x[1];
    for(i=2;i<=len;i++)
    {
        while(i-list[head]>Lim)head++;
        temp=x[i]-x[list[head]];
        if(temp>ans)ans=temp;
        while(tail>=head && x[list[tail]]>=x[i])
            tail--;
        list[++tail]=i;
    }
    return ans;
}
/*void TestOut(long int a[][ARRAY],long int l1,long int l2)
{
    long int i,j;
    for(i=1;i<=l1;i++)
    {
        for(j=1;j<=l2;j++)
            printf("%6d ",a[i][j]);
        printf("\n");
    }
    printf("\n");
}*/
long int InterConnect(long int n,long int m,long int a[][ARRAY])
{
    long int i,j,k,num,max,bx,by,ex,ey,k1,k2,d,nowx,nowy,temp,ans;
    long int dis[ARRAY][ARRAY],fixed[ARRAY][ARRAY],part[ARRAY][ARRAY];
    long int value[BLOCK],prex[ARRAY][ARRAY],prey[ARRAY][ARRAY];
    long int conx[BLOCK][SURROUND],cony[BLOCK][SURROUND],ncon[BLOCK];

    for(i=1;i<=n;i++)for(j=1;j<=m;j++)
        if(a[i][j]>=0)
            fixed[i][j]=1;
        else 
            fixed[i][j]=0;

    while(1)
    {
        num=Partition(n,m,a,fixed,part,value);
        GetContour(n,m,fixed,part,conx,cony,ncon);
        max=-MAXN;
        for(i=1;i<=num-1;i++)
            for(k1=1;k1<=ncon[i];k1++)
            {
                SPFA(n,m,conx[i][k1],cony[i][k1],a,fixed,dis,prex,prey);
                for(j=i+1;j<=num;j++)
                    for(k2=1;k2<=ncon[j];k2++)
                    {
                        d=dis[conx[j][k2]][cony[j][k2]]-a[conx[j][k2]][cony[j][k2]];
                        if(d>max && value[part[conx[i][k1]][cony[i][k1]]]+d>=0 && value[part[conx[j][k2]][cony[j][k2]]]+d>=0)
                        {
                            max=dis[conx[j][k2]][cony[j][k2]]-a[conx[j][k2]][cony[j][k2]];
                            bx=conx[i][k1];
                            by=cony[i][k1];
                            ex=conx[j][k2];
                            ey=cony[j][k2];
                        }
                    }
            }

        if(max==-MAXN)break;

        SPFA(n,m,bx,by,a,fixed,dis,prex,prey);
        nowx=ex;
        nowy=ey;
        while(nowx!=0 && nowy!=0)
        {
            fixed[nowx][nowy]=1;
            temp=nowx;
            nowx=prex[nowx][nowy];
            nowy=prey[temp][nowy];
        }
    }
    ans=-MAXN;
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            if(a[i][j]>ans)
                ans=a[i][j];
    for(i=1;i<=num;i++)if(value[i]>ans)ans=value[i];
    return ans;
}
void SPFA(long int n,long int m,long int bx,long int by,long int a[][ARRAY],long int fixed[][ARRAY],long int dis[][ARRAY],long int prex[][ARRAY],long int prey[][ARRAY])
{
    long int i,j,h,t,x,y,tx,ty,sx[100000],sy[10000],in[100][100];
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
        {
            dis[i][j]=-MAXN;
            in[i][j]=0;
            prex[i][j]=0;
            prey[i][j]=0;
        }
    sx[1]=bx;
    sy[1]=by;
    dis[bx][by]=0;
    h=t=1;
    in[bx][by]=1;

    while(h<=t)
    {
        x=sx[h];
        y=sy[h];
        for(i=1;i<=4;i++)
        {
            tx=x+dx[i];
            ty=y+dy[i];
            if(tx>=1 && tx<=n && ty>=1 && ty<=m && 
                ((fixed[x][y]==0 && (tx!=bx || ty!=by)) || (x==bx && y==by && fixed[tx][ty]==0)) &&
                dis[x][y]+a[tx][ty]>dis[tx][ty])
            {
                dis[tx][ty]=dis[x][y]+a[tx][ty];
                prex[tx][ty]=x;
                prey[tx][ty]=y;
                if(in[tx][ty]==0)
                {
                    in[tx][ty]=1;
                    t++;
                    sx[t]=tx;
                    sy[t]=ty;
                }
            }
        }
        in[x][y]=0;
        h++;
    }
}
void GetContour(long int n,long int m,long int fixed[][ARRAY],long int part[][ARRAY],long int conx[100][150],long int cony[100][150],long int ncon[100])
{
    long int i,j,k,p,num;

    for(i=0;i<BLOCK;i++)
    {
        memset(conx[i],0,sizeof(long int)*SURROUND);
        memset(cony[i],0,sizeof(long int)*SURROUND);
    }
    memset(ncon,0,sizeof(long int)*BLOCK);

    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            if(fixed[i][j]==1)
            {
                p=part[i][j];
                for(k=1;k<=4;k++)
                {
                    if(i+dx[k]>=1 && i+dx[k]<=n && j+dy[k]>=1 && j+dy[k]<=m && fixed[i+dx[k]][j+dy[k]]==0)
                    {
                        ncon[p]++;
                        conx[p][ncon[p]]=i;
                        cony[p][ncon[p]]=j;
                        break;
                    }

                }
            }
}
long int Partition(long int n,long int m,long int a[][ARRAY],long int fixed[][ARRAY],long int part[][ARRAY],long int value[])
{
    long int i,j,num;
    for(i=1;i<=n;i++)
        memset(part[i],0,sizeof(long int)*(m+1));

    num=0;
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            if(fixed[i][j]==1 && part[i][j]==0)
            {
                num++;
                value[num]=DFS(i,j,num,n,m,a,fixed,part);
            }
    return num;
}
long int DFS(long int i,long int j,long int num,long int n,long int m,long int a[][ARRAY],long int fixed[][ARRAY],long int part[][ARRAY])
{
    if(part[i][j]!=0 || fixed[i][j]==0)return 0;
    long int sum=0;
    part[i][j]=num;
    if(i-1>=1)sum+=DFS(i-1,j,num,n,m,a,fixed,part);
    if(i+1<=n)sum+=DFS(i+1,j,num,n,m,a,fixed,part);
    if(j-1>=1)sum+=DFS(i,j-1,num,n,m,a,fixed,part);
    if(j+1<=m)sum+=DFS(i,j+1,num,n,m,a,fixed,part);
    return sum+a[i][j];
}
posted @ 2013-09-30 16:49  zjoe  阅读(193)  评论(1编辑  收藏  举报