user-0 提问: 求大大看看  哪里错了  提交上去是RE  改了好多次实在是没办法了 求指导

回复:
user: user-0
content: 表示把那个结构体的数组开大  AC了....无语...这道题目作得都快哭了  我再试试回溯法

user: user-0
content: 不好意思  这2天没怎么碰电脑  谢谢上面2位大神的解释  我马上改正看看

user: user-1
content: :( 啊，老bai说到了要点，确实是数组开小了。如果路径长度为21，最少要开到352716。20的话也要184756。老白牛逼啊，一眼看出是组合数！我最初的程序的没有保存路径，忽略了这个问题。  另外，确实发现了大量输出时cout的速度对比printf有点悲剧。  对于给定的规模，程序运行的主要时间用在输出上，算法的改进效果并不明显，楼主最初的程序就把10000换成200000，提交应该就可以了。  这个是用回溯法的，仿佛略快一些，说明剪枝的收入还是大于成本(含递归调用成本)的。如果把路径最大长度放松到28，关闭掉输出，剪了枝的回溯法就表现出明显的的速度优势了(15秒 vs 200秒) 。如果在这个基础上再去掉递归，改为循环，速度提升会更大。可惜在巨额的输出成本下面，这样的优化可以忽略不计了。  #include <iostream> #include <vector> #include <algorithm>  using namespace std;  const int max_path_len=31;  int n;   int Ax,Ay, Bx,By; // coordinates of A & B struct path_t{ 	path_t(int path):path(path) 	{ 		sortby=0; 		int dx=0; 		for(int i=0; i<n; ++i) 		{ 			sortby<<=1; 			if(!(path&(1<<i)) ) 			   ++sortby; 		} 	}  	bool operator <(const path_t&rhs)const{ return sortby<rhs.sortby; } 	int path; 	int sortby; }; vector<path_t> paths;   void output();    void back_track(int i, int path, int curx, int cury) {       if(i==n)       {            paths.push_back(path_t(path));            return;       }       // 向右，如果可行       if(curx<Bx)            back_track(i+1, path, curx+1, cury);       // 向下，如果可行       if(cury<By)           back_track(i+1, path|(1<<i), curx, cury+1); }   int main() { 	int path=0,curx,cury; 	 //	cout<<"Enter coordinates for point A:"; 	cin>>Ax>>Ay; //	cout<<"Enter coordinates for point B:"; 	cin>>Bx>>By; 	//Ax=Ay=0; Bx=By=14;  	n=By-Ay+Bx-Ax;  	if(Ax>Bx || Ay>By || 	   n>max_path_len) 	{ 		cout<<"Input is illegal, exiting..."; 		return -1; 	}	 	curx=Ax, cury=Ay; 	paths.reserve(200000);      back_track(0,path,curx,cury); 	std::sort(paths.begin(), paths.end()); 	printf("%d\n",paths.size()); 	output();     return 0; }  //#define USE_COUT void output() { 	int path, curx, cury; 	for(unsigned i=0; i<paths.size(); ++i) 	{ 		path=paths[i].path; 		curx=Ax, cury=Ay; 		 #ifdef USE_COUT 		cout<<'('<<curx<<','<<cury<<')'; 		for(int j=0; j<n; ++j) 		{ 			if(path&(1<<j)) 				++cury; 			else 				++curx; 			cout<<"->("<<curx<<','<<cury<<')'; 		} 		cout<<endl; #else 		printf("(%d,%d)",curx,cury);	 		for(int j=0; j<n; ++j) 		{ 			if(path&(1<<j)) 				++cury; 			else 				++curx; 			printf("->(%d,%d)",curx,cury); 		} 		putchar('\n'); #endif 	} 	printf("%d\n",paths.size()); }

user: user-1
content: #include<stdio.h> #include<stdlib.h>  struct success_step {     long long step;     long long sum; }ss[10000];  int cmp(const void *a,const void *b) {     struct success_step *p=(struct success_step *)a;     struct success_step *q=(struct success_step *)b;     return q->sum-p->sum; } int main() {     int ax,ay,bx,by;     long long step=0,n;     int max,i=0,j,x,y,k=0,xi,yi,j1,m;       scanf("%d%d%d%d",&ax,&ay,&bx,&by);     max=by+bx-ax-ay;     x=bx-ax;y=by-ay;k=0;  again:     while(step!=(1LL<<max))//列举出 可能的 移动方式     {         xi=0;yi=0;n=0;  		++step;         for(j=0;j<max;j++)         {             if(step&(1ll<<j))             	yi++;             else xi++;             if(xi>x || yi>y)                 goto again;         }         if(xi==x&&yi==y)         {             ss[k].step=step;             for(j=0,j1=max;j<max;j1--,j++){                 //ss[k].step1[j]=step[j];                 if(step&(1ll<<j))n+=(1ll<<j1);             }             ss[k++].sum=n;         }     }     qsort(ss,k,sizeof(ss[0]),cmp);//排序 先x后y     for(i=0;i<k;i++)//打印     {         xi=ax;yi=ay;         printf("(%d,%d)",ax,ay);         for(j=0;j<max;j++){             if(ss[i].step & (1LL<<j))yi++;             else xi++;             printf("-->(%d,%d)",xi,yi);         }         putchar('\n');     }     printf("%d\n",k);     return 0; }   试试这个，直接套你的方法得来的。

user: user-1
content: 以A=(0,0), B=(4,8)为例，有效的路径有495条。  而且这明显是个子集树问题（不是排序树问题),解不大于2^n（与n！无关), 楼主有想到pow(2,j1)之类，说明他是理解这一点的。另pow(2,j1)语义上等价于1<<j1, 后者快得多。楼主的意思确实是系统的搜索整个解空间，就是设法把回溯法以循环（而非递归)表达出来。  检查了一下，楼主的算法是正确的，得到的结果和我的相同，只是我的少了一个排序，而排序确实是题目要求的。给楼主稍微修改了一下，理论上可以略微提高一下效率。  int main() {     int ax,ay,bx,by;     int step[21];     int max,i=0,j,x,y,k=0,xi,yi,j1,n,m, dead;       scanf("%d%d%d%d",&ax,&ay,&bx,&by);     max=by+bx-ax-ay;     x=bx-ax;y=by-ay;k=0;     for(j=0;j<max;j++)         step[j]=0;     while(i!=max)//列举出 可能的 移动方式     {         dead=0;         i=0;xi=0;yi=0;n=0;         while(i<max&&++step[i]==2)             step[i++]=0;          for(j=0;j<max;j++)         {             if(step[j]==0)             	xi++;             else yi++;             if(xi>x || yi>y) // 这里加了个合法性剪枝                 ++dead;         }         if(dead)             continue;         if(xi==x&&yi==y)         {             for(j=0,j1=max;j<max;j1--,j++){                 ss[k].step1[j]=step[j];                 if(step[j]==1)n+=1<<j1; // pow替换成等价的 1<<j1             }             ss[k++].sum=n;         }     }     qsort(ss,k,sizeof(ss[0]),cmp);//排序 先x后y     for(i=0;i<k;i++)//打印     {         xi=ax;yi=ay;         printf("(%d,%d)-->",ax,ay);         for(j=0;j<max;j++){             if(ss[i].step1[j]==0)xi++;             else yi++;             if(j!=max-1)printf("(%d,%d)-->",xi,yi);             else printf("(%d,%d)\n",xi,yi);             }     }     printf("%d\n",k);     return 0; }   我估计不是楼主的算法错了，是题目坑爹。你把那个21放大些，比如改为40再看看。如果要节省内存，改成char。

user: user-2
content: 组合数学中两个已序区间的合并问题，具体算法忘了，查查吧。

user: user-3
content: struct success_step {     int step1[21];     int sum; }ss[10000];  设abs(ax-bx)=A 和abs(ay-by)=B的时候有 于是有方法数：C(A+B,A)  A=B=10的时候 C(20,10) = 184756  所以，你的数组开小了。。。  其它case自己再看看吧。

user: user-1
content: 完整的程序。  原来的程序错在哪里真不重要。应该学会利用前人的智慧，用系统性的方法去思考和解决系列的问题。牛顿都能不以站在前人的肩膀上为耻，何况我等庸众？    #include <iostream>  using std::cin; using std::cout; using std::endl;  struct point{ int x,y; }; const int max_path_len=20;  int n;  point steps[max_path_len], B;   void output_path();    void back_track(int i) {       if(i==n)       {            // we get a path in steps, output it            output_path();            return;       }       steps[i]=steps[i-1]; //复制前面点的位置，后面修改       // 向右，如果可行       if(steps[i].x<B.x)       {            ++steps[i].x;            back_track(i+1);            --steps[i].x;       }       // 向下，如果可行       if(steps[i].y<B.y)       {           ++steps[i].y;           back_track(i+1);           --steps[i].y;        } }     int main() { 	cout.sync_with_stdio(false); 	 //	cout<<"Enter coordinates for point A:"; //	cin>>steps[0].x>>steps[0].y; //	cout<<"Enter coordinates for point B:"; //	cin>>B.x>>B.y; 	steps[0].x=steps[0].y=0; //Point A; 	B.x=4; B.y=8;    	n=B.y-steps[0].y+B.x-steps[0].x;  	if(steps[0].x>B.x || steps[0].y>B.y || 	   n>max_path_len) 	{ 		cout<<"Input is illegal, exiting..."; 		return -1; 	}	      back_track(1);     return 0; }   void output_path() { 	for(int i=0; i<n; ++i) 	{ 		cout<<'('<<steps[i].x<<','<<steps[i].y<<')'<<"->"; 		 	} 	if(n!=0) 		cout<<'('<<B.x<<','<<B.y<<')'; 	cout<<endl; }

user: user-1
content: 这个问题可以用回溯法来解。可以把它作为学习回溯法的一个好示例。  记A的坐标为(Xa,Ya), B的坐标为(Xb,Yb),其中Xb>=Xa, Yb>=Ya. 那么一条合法完整的路径的长度等于Xb+Yb-Xa-Ya, 记n=Xb+Yb-Xa-Ya.  取一个长度为Xb+Yb-Xa-Ya的Point数组。每个位置最多有向上和向右两种选择，这是一个状态空间不大于2^n的子集树搜索问题。   struct point{ int x,y; };  point steps[n];  void back_track(int i) {       if(i==n)       {            // we get a path in steps, output it            output_path();            return;       }       steps[i]=steps[i-1]; //复制前面点的位置，后面修改       // 向右，如果可行       if(steps[i].x<B.x)       {            ++steps[i].x;            back_track(i+1);            --steps[i].x;       }       // 向下，如果可行       if(steps[i].y<B.y)       {           ++steps[i].y;           back_track(i+1);           --steps[i].y;        } }   int main() {     steps[0]=A;     back_track(1); }   这个要求输出所有路径，所以不存在什么优化问题，只要合法性剪枝就好。另外并cout不会成为你的问题。