homework-02
最大子数组和问题的扩展
综述
这次作业是在第一次作业基础上进行的扩展。需要完成的任务有如下几个:
- 以命令行参数的形式传入输入文件名等控制参数,并对输入文件的正确性进行检验,保证程序不会崩溃。
- 处理一维和二维子矩阵的情况
- 处理数组左右边界相连或上下边界相连的情况,以-v表示左右相连,-h表示上下相连。这两个参数可以同时出现。
- 将最大子矩阵的“矩阵”条件降低为“连通子块”,上下或左右相邻视为连通。以-a表示。
- 考虑-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];
}