第一题:给定nm的矩阵,每个格子内有一个数值,要求从第一行到第n行的没一个格子都能到的路线上最大值的最小值。
思路:一开始写了一个裸裸的深搜,但是tle了(当时自己以为不会。。。)。后来听说二分答案,然后就有了一点思路。可以找到所有数值中的最大值和最小值,然后对于mid为标准,比mid小的格子可以走,比mid大的不可以,然后从一头进行floodfill,如果能到另一头,就把范围缩小,一直到l>=r-1,然后输出答案。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int p[1001][1001]={0},dx[4]={0,0,1,-1},dy[4]={1,-1,0,0},m,n,t; bool map[1001][1001]={false},visit[1001][1001]={false},ff=false; void dfs(int x,int y) { int i,j,xx,yy; if (visit[x][y]) return; visit[x][y]=true; if (x==n) { t=m; ff=true; return; } for (i=0;i<=3;++i) { xx=x+dx[i]; yy=y+dy[i]; if (xx>0&&xx<=n&&yy>0&&yy<=m&&map[xx][yy]) { dfs(xx,yy); if (ff) return; } } } int main() { freopen("murder.in","r",stdin); freopen("murder.out","w",stdout); int i,j,minn,maxn=0,mid,ans; scanf("%d%d",&n,&m); minn=2100000000; for (i=1;i<=n;++i) for (j=1;j<=m;++j) { scanf("%d",&p[i][j]); if (p[i][j]>maxn) maxn=p[i][j]; if (p[i][j]<minn) minn=p[i][j]; map[i][j]=true; } while(minn<maxn-1) { mid=(minn+maxn)/2; memset(visit,false,sizeof(visit)); for (i=1;i<=n;++i) for (j=1;j<=m;++j) { if (p[i][j]<=mid) map[i][j]=true; else map[i][j]=false; } t=0; ff=false; dfs(1,1); if (t<m) minn=mid; else ans=maxn=mid; } printf("%d\n",ans); fclose(stdin); fclose(stdout); }
这里有一点问题,就是要注意return,否则可能re。。。
第二题:给定一些文件的路径,不同的层之间用/隔开,最后输出一个清晰的文件夹形式的目录。
思路:很裸的字符串操作,放到树里面,但因为结点很少,所以不需要什么高级的优化就可以。很简单,不过有些细节问题要注意,不能出错。调了好久。。。读入的时候边分离文件名边从树里找到相对应的路径,从父亲往孩子一层层找就可以了,如果这个父亲没有这个孩子,就把它加进去,然后继续。输出的时候对一个结点的孩子排一下序,然后递归输出就可以了。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; struct use{ int fa,son[16]={0},len; char ch[10]; }tree[760000],na; char ss[1000]; int comp(struct use x,struct use y) { int i,l; l=min(x.len,y.len); for (i=0;i<l;++i) { if (x.ch[i]<y.ch[i]) return -1; if (x.ch[i]>y.ch[i]) return 1; } if (x.len>l) return 1; if (y.len>l) return -1; return 0; } void dfs(int i,int ceng) { int j,k,t; for (j=1;j<=5*(ceng-1);++j) { if (j%5==1) printf("|"); else printf(" "); } if (ceng>0) printf("|----"); for (j=0;j<tree[i].len;++j) printf("%c",tree[i].ch[j]); printf("\n"); if (tree[i].son[0]==0) return; for (j=1;j<=tree[i].son[0]-1;++j) for (k=tree[i].son[0]-1;k>=j;--k) { if (comp(tree[tree[i].son[k]],tree[tree[i].son[k+1]])>0) { t=tree[i].son[k];tree[i].son[k]=tree[i].son[k+1];tree[i].son[k+1]=t; } } for (j=1;j<=tree[i].son[0];++j) dfs(tree[i].son[j],ceng+1); } int main() { freopen("file.in","r",stdin); freopen("file.out","w",stdout); int n,i,j,k,l,t,ln,top=0; bool f=false; scanf("%d",&n); for (i=1;i<=n;++i) { scanf("%s%*c",&ss); l=strlen(ss); t=0;j=0; while(j<l) { ln=0; while(ss[j]!='/'&&j<l) { na.ch[ln]=ss[j];++ln;++j; } ++j; na.len=ln;f=false; for (k=1;k<=tree[t].son[0];++k) { if (comp(tree[tree[t].son[k]],na)==0) { t=tree[t].son[k]; f=true; break; } } if (!f) { ++top;tree[top].fa=t;tree[top].len=ln; for (k=0;k<ln;++k) tree[top].ch[k]=na.ch[k]; ++tree[t].son[0]; tree[t].son[tree[t].son[0]]=top; t=top; } } } dfs(1,0); fclose(stdin); fclose(stdout); }
第三题:有n个函数,每一个函数有一个效果值,这n个函数是环形排列的,然后要求从n个函数中选出m个不相邻的函数使其效果值之和最大。
思路:这个题在测试的时候直接放弃了,想写一个深搜,结果十分钟赤裸裸的写渣了。下午先用了dp,结果第一次只写对了几个点。后来看了dada的思路,明白了一些,换了循环顺序,先循环取几个数,然后从能取的数的位置用二维数组更新到这个位置上取多少个数的值,因为是环形的,所以要做两遍(一遍是1~n-1,一遍是2~n),然后取较大值输出。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int f[5001][5001],a[5001]={0},n,m; int work(int l,int r) { int i,j; memset(f,128,sizeof(f)); for (i=l;i<=n;++i) f[i][1]=max(f[i-1][1],a[i]); for (i=2;i<=m;++i) for (j=l+(i-1)*2;j<=r;++j) f[j][i]=max(f[j-1][i],f[j-2][i-1]+a[j]); return f[r][m]; } int main() { freopen("compile.in","r",stdin); freopen("compile.out","w",stdout); int i,j; scanf("%d%d",&n,&m); for (i=1;i<=n;++i) scanf("%d",&a[i]); if (m>n/2) printf("Error!\n"); else printf("%d\n",max(work(1,n-1),work(2,n))); fclose(stdin); fclose(stdout); }
终于弄明白了这道题的正解,被dada神称作贪心的算法。题解里面说用到了残余流的思想,可我根本不会。。。这道题用堆来维护,保存一个值的左右邻结点(一开始就是数组中相邻的两个,但后来不断有根取走后,就是数组中相邻的有值的位置了)和一个值相对应的在堆中的位置(堆用完全二叉树建)。每次取走大根堆的根,然后把这个节点的值更新成它的左右邻结点的值的和-这个点的值(因为我们只能取最早数组中不相邻的两个点,这样如果取走了大根堆的根是由上一个根的左右邻结点更新的,我们就会把第一个根的信息抛掉,正好我们也不再需要这个结点了),再将左右邻结点从堆中删掉(赋成极小值,然后down下去),这样只要取m次根的和就是答案了。
第一次练习了堆,觉得是个很实用的东西。
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; int heap[200001]={0},a[200001]={0},pos[200001]={0},l[200001]={0},r[200001]={0},n,m; void up(int x) { while(x>1) { if (a[heap[x]]>a[heap[x/2]]) { swap(heap[x],heap[x/2]); swap(pos[heap[x]],pos[heap[x/2]]); x=x/2; } else return; } } void down(int x) { int i; while(x*2<=n) { if(x*2==n||a[heap[x*2]]>a[heap[x*2+1]]) i=x*2; else i=x*2+1; if (a[heap[x]]<a[heap[i]]) { swap(heap[x],heap[i]); swap(pos[heap[x]],pos[heap[i]]); x=i; } else return; } } int main() { int i,j,ans=0; scanf("%d%d",&n,&m); if (m>n/2) printf("Error!\n"); else { for (i=1;i<=n;++i) { scanf("%d",&a[i]); l[i]=i-1;r[i]=i+1; heap[i]=i;pos[i]=i; up(i); } l[1]=n;r[n]=1; for (i=1;i<=m;++i) { j=heap[1]; ans+=a[j]; a[j]=a[l[j]]+a[r[j]]-a[j]; a[l[j]]=-10000;down(pos[l[j]]); a[r[j]]=-10000;down(pos[r[j]]); down(1); l[j]=l[l[j]];r[j]=r[r[j]]; l[r[j]]=j;r[l[j]]=j; } printf("%d\n",ans); } }