1、Noip提高组Day2解题报告第二题:花匠(动态规划)1.令Si1表示以i为结尾,且降序到达ai的最长抖动序列长度;令Si0表示以i为结尾,且升序到达ai的最长抖动序列长度。依然设数组Si0/1,但考虑如下递推公式:(1)ai+1ai:Si+10=max(Si1+1,Si0);Si+11=Si1;(2)ai+1ai:Si+10=Si0;Si+11=max(Si0+1,Si1);(3)ai+1= =ai:Si+10=Si0;Si+11=Si1;S10=S11=1.算法优化后,再一次编写程序,O(n)的时间复杂度,当然是顺利AC了,代码如下:2.这道题明显可以用类似最长上升子序列的动态规划求解,易
2、得思路如下:用f(i,0)表示以i为结尾的且最后一段上升的子序列最大长度,f(i,1)表示表示以i为结尾的且最后一段下降的子序列最大长度,那么答案明显就是maxf(i,0),f(i,1)方程:f(i,0)=maxf(j,1)+1 0=ji且hjhif(i,1)=maxf(j,0)+1 0=jhi边界:f(0,0)=f(0,1)=0如果直接DP毫无疑问复杂度是O(n2),会TLE,但是,考虑到我们每次取最值时候取得都是一个区间里的数,如f(i,0)=maxf(j,1)+1 0=ji且hjhi取得就是区间0,hi-1里的最值,所以可以使用线段树或者是BIT(树状数组)来优化,这样复杂度就是O(n
3、log n),可以过全部数据。这道题还有一个解法,直接求拐点数目,然后就可以神奇的做到O(n)了,由于我找不到满意的证明,就不发上来了。代码(DP+BIT)(Cpp):#include #include #include using namespace std;#define MAXN 100010#define lowbit(x)(x)+1)&x)#define MAXH 1000010#define For(i,x) for (int i=x;i;i-=lowbit(i)#define rep(i,x) for (int i=x;i=maxh;i+=lowbit(i)int t0MAXH,
4、t1MAXH;int hMAXN,n,maxh=0;int fMAXN2,ans=0;void Add0(int x,int y) rep(i,x) t0i=max(t0i,y);void Add1(int x,int y) rep(i,x) t1i=max(t1i,y);int Max0(int x) int rec=0; For(i,x) rec=max(rec,t0i); return rec;int Max1(int x) int rec=0; For(i,x) rec=max(rec,t1i); return rec;int main() scanf(%d,&n); for (int
5、 i=0;i+n;) scanf(%d,&hi); maxh=max(maxh,+hi); fi0=fi1=1; maxh+; memset(t0,0,sizeof(t0),memset(t1,0,sizeof(t1); for (int i=0;i+n;) fi0=max(Max0(hi-1)+1,fi0); fi1=max(Max1(maxh-hi-1)+1,fi1); Add0(hi,fi1),Add1(maxh-hi,fi0); ans=max(ans,max(fi0,fi1); printf(%dn,ans); return 0;其实再进一步分析,发现问题可以简化成为求转折点数的问题
6、,又是一个O(n),对序列缩点,连续递减的点和连续递增的点是可以缩到一个代表性的点上的,比如说样例给的5 3 2 1 2,可以缩成5,1,2或3,1,2或2,1,2,即5 3 2这三个连续递减的点实际上可以由一个点代替,1是一个转折点,于是你也可以说是找转折点个数。第三题:华容道(最短路)这道题的数据范围是n=30,所以,我们可以看到,裸的O(n4)的BFS对于求解q较小的情况是无压力的,但是在q大的情况下,毫无疑问会TLE,明显,在q较大的情况下,我们需要将每次BFS中重复搜索的冗余信息除去,所以我们可以先分析题目性质:(这里称要移动的棋子为目标棋子)首先,如果要移动目标棋子,那么我们首先必
7、须要将空格移到该棋子的上下左右四个方向上相邻位置之一,然后才可以移动该棋子。然后,我们分析该棋子移动时候的性质:棋子每次可以移动,仅当空格位于其相邻位置的时候,每次移动完棋子,空格总会在棋子相邻的位置,那么我们发现,对于棋子在某一位置,然后空格又在其四个方向上某一相邻位置时,棋子要想某一方向移动一个时的花费的步数是一定的,那么,就可以先进行一次预处理,预处理出对于目标棋子在上述条件下每次移动所需的步数。然后,预处理完成之后,我们会发现每次查询都会变成一个求最短路的问题,用Dijstra或SPFA的话,可以在时限范围内解决。实现:定义一个数组Stepxykh,表示目标棋子在位置(x,y)且空格在
8、目标棋子的k方向上的相邻格子时,目标棋子往h方向移动1格所需的步数,然后用状态xyk作为节点建图,用各个状态的关系连边,每次询问时重新定义一个源点跟终点,跑最短路就可以得出答案。(预处理时跑n2次O(n2)的BFS就可以了)复杂度(Dijstra):(n4+n2 log n)复杂度(SPFA):(n4+n2)代码(SPFA)(Cpp):#include #include #include #include using namespace std;#define MAXN 32#define MAXV 50010#define inf (1t=t,p-d=d; p-next=heads; hea
9、ds=p;int MapMAXNMAXN,n,m,q,ex,ey,sx,sy,tx,ty;int vMAXNMAXN4,V=0;int distMAXNMAXN,MoveMAXNMAXN44;int DistMAXV;bool fMAXV;int S,T;struct node int x,y; node (int _x,int _y):x(_x),y(_y) ;queueQ;int Bfs(int Sx,int Sy,int Tx,int Ty) if (Sx=Tx&Sy=Ty) return 0; while (!Q.empty() Q.pop(); for (int i=0;i+n;)
10、for (int j=0;j+m;) distij=inf; distSxSy=0; Q.push(node(Sx,Sy); while (!Q.empty() node u=Q.front(); Q.pop(); for (int i=0;iDisty; ;priority_queueint,vector,CmpPq;int spfa() for (int i=0;i+next) if (Distp-tDistu+p-d) Distp-t=Distu+p-d; fp-t=true; Pq.push(p-t); return DistTnmq; memset(Map,0,sizeof(Map)
11、; for (int i=0;i+n;) for (int j=0;j+Mapij; for (int k=0;k4;k+) vijk=+V; for (int i=0;i+n;) for (int j=0;j+m;) for (int k=0;k4;k+) for (int h=0;h4;h+) Moveijkh=inf; for (int i=0;i+n;) for (int j=0;j+m;) if (Mapij) Mapij=0; for (int k=0;k4;k+) if (Mapi+dirk0j+dirk1) for (int h=0;h4;h+) if (Mapi+dirh0j
12、+dirh1) Moveijkh=Bfs(i+dirk0,j+dirk1,i+dirh0,j+dirh1)+1; Mapij=1; memset(head,0,sizeof(head); for (int i=0;i+n;) for (int j=0;j+m;) for (int k=0;k4;k+) for (int h=0;h4;h+) if (Moveijkhexeysxsytxty; if (sx=tx&sy=ty) cout0endl; continue; S=+V; T=+V; if (Mapsxsy=0|Maptxty=0) cout-1endl; continue; Mapsxsy=0; for (int i=0;i4;i+) if (Mapsx+diri0sy+diri1) int D=Bfs(ex,ey,sx+diri0,sy+diri1); if (Dinf) AddEdge(S,vsxsyi,D); Mapsxsy=1; for (int i=0;i4;i+) if (Maptx+diri0ty+diri1) AddEdge(vtxtyi,T,0); coutspfa()endl; return 0;
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1