DFS不让用数组的后果~~
背景:
今天有一道题是这样的
小明要看电视,给出今天的节目列表,问最多可以完整观看多少个节目,节目时间均为整点
输入:总共节目数 节目1开始时间 节目1结束时间 节目2开始时间 节目2结束时间
例:2 1 3 5 10
限时1000ms
显然应该使用DFS暴搜的一道题
关键是。。。老师说不让用数组。
那么问题来了:
- 如何存储目前的搜索状态
- 如何存储节目列表
该怎么办呢? 于是开始用各种阴招:
- 利用bitmap表示24个小时,绕开数组
- 由于每一层的节目时间是相同的,那么可以通过巧妙设置栈,解决存储问题
其实之前还想了:
每一层做一个dispatcher(需要vector)将任务拆解成小的task或者coroutine,然后用异步实现(需要socket)暴力多线程fork写临时文件把数据写回到stdin
解释:
- gcc和vc都支持inline函数和寄存器传参,在x86 x64 armv7 aarch64架构上可以保证用三个寄存器传递参数,从而使每一个函数建立的栈帧不会被破坏。
- 递归调用handler,并且handler中没有类似于alloca的动态栈分配,每一次调用的函数建立的栈帧完全相同,进而可以一步一步改写调用堆栈上所有函数的a和b。
- 我的dfs算法中任一确定层的a b是固定的,因此每次handler执行到同一深度的时候,都会访问到相同的a和b。
该代码在x64 gcc -O0,x64 gcc -O3,x86 vc Release编译选项下均通过。
理论上只要处理器模型相似,并且编译器不像vc Debug下默认设置一样在每个函数起始处将局部变量清除,该代码均可正常运行。
两排notUsed是为了孤立出a和b,防止被意外覆写。
(PS:实际上这个思路类似于UAF,搞pwn的童鞋应该会明白哒)
#include <stdio.h> #include <math.h> #include <stdlib.h> #include <stddef.h> #ifdef __GNUC__ #define REGPARM __attribute__((regparm(3))) #else #define REGPARM __fastcall #endif #ifdef __GNUC__ #define PLEASE_INLINE __attribute__((always_inline)) #else #define PLEASE_INLINE inline #endif PLEASE_INLINE char getBit(int bitmap,int pos){ return (bitmap & (1 << pos)) ? 1 : 0; } PLEASE_INLINE int hasBitOverlap(int bitmap1,int bitmap2){ int i; for(i = 0;i < 32;i++){ if( getBit(bitmap1,i) == 1 && 1 == getBit(bitmap2,i)){ return 1; } } return 0; } PLEASE_INLINE int genBit(int a, int b){ int i,ret = 0; for(i = a; i< b; i++){ ret |= (1 << i); } return ret; } int oriDepth = 0; int *pa = 0; int *pb = 0; int hasRead = 0; int REGPARM handler(int maxDepth, int curTimeTable,int curK){ int notUsed1, notUsed2, notUsed3, notUsed4, notUsed5, notUsed6, notUsed7, notUsed8, notUsed9, notUsed10; int a, b, i, ret1, ret2; int _notUsed1, _notUsed2, _notUsed3, _notUsed4, _notUsed5, _notUsed6, _notUsed7, _notUsed8, _notUsed9, _notUsed10; if (maxDepth == 0) { if (!hasRead) { ptrdiff_t pdiffa = pa - &a; ptrdiff_t pdiffb = pb - &b; int *cura = pa, *curb = pb; for (i = 0; i < oriDepth; i++) { scanf("%d", cura); scanf("%d", curb); cura += pdiffa; curb += pdiffb; } hasRead = 1; } return curK; } pa = &a; pb = &b; ret1 = handler(maxDepth - 1, curTimeTable, curK); a = *&a; b = *&b; genBit(a,b); if(hasBitOverlap(curTimeTable,genBit(a,b))){ return ret1; } else{ ret2 = handler(maxDepth - 1, curTimeTable | genBit(a,b), curK + 1); return ret1 > ret2 ? ret1 : ret2; } } int main(){ int n,i,a,b, lastFound = -1; scanf("%d",&n); oriDepth = n; printf("%d",handler(n, 0, 0)); return 0; }
附:当时提交的版本(部分使用数组):
#include <stdio.h> #include <math.h> #include <stdlib.h> char getBit(int bitmap,int pos){ return (bitmap & (1 << pos)) ? 1 : 0; } int hasBitOverlap(int bitmap1,int bitmap2){ int i; for(i = 0;i < 32;i++){ if( getBit(bitmap1,i) == 1 && 1 == getBit(bitmap2,i)){ return 1; } } return 0; } int genBit(int a, int b){ int i,ret = 0; for(i = a; i< b; i++){ ret |= (1 << i); } return ret; } int *programList = 0; int inputHelperA(int curDepth){ return programList[curDepth * 2]; } int inputHelperB(int curDepth){ return programList[curDepth * 2 + 1]; } int handler(int maxDepth, int curTimeTable,int curK){ int a, b,ret1,ret2; if(maxDepth == 0) return curK; a = inputHelperA(maxDepth); b = inputHelperB(maxDepth); genBit(a,b); if(hasBitOverlap(curTimeTable,genBit(a,b))){ return handler(maxDepth - 1, curTimeTable, curK); } else{ ret1 = handler(maxDepth - 1, curTimeTable, curK); ret2 = handler(maxDepth - 1, curTimeTable | genBit(a,b), curK + 1); return ret1 > ret2 ? ret1 : ret2; } } int main(){ int n,i,a,b, lastFound = -1; scanf("%d",&n); programList = (int *)malloc((n+1) * 2 * sizeof(int)); for(i = n; i > 0; i--){ scanf("%d%d", &a,&b); programList[2 * i] = a; programList[2 * i + 1] = b; } printf("%d",handler(n, 0, 0)); return 0; }