HDU4026
来源:互联网 发布:什么是物联网大数据 编辑:程序博客网 时间:2024/06/05 14:49
HDU4026Unlock the Cell Phone
现代手机都有解锁模式,比如在一个3*3的键盘上,你可以选定一个起始点,点上去然后滑动到它相邻的点上。比如起始为1可以滑动到2,4,5,6,8上,因为1到这些点之间都没有其他点。但是你不能直接从1滑动到7或3,因为2或4你还没走过。当你走到了一个点之后,你就能从它上面划过去了。如下面第3个图1->5->9->6->4,因为5已经走过了,所以可以从6滑到4.
现在有一个特殊的手机,它有3种点:0号普通点,能走过上去且能从上面滑过.1号被禁止的点,不能走上且也不能从上面滑过.2号不活跃的点:你不能走上去但是能从上面滑过.
每个点只能走上去1次,现在问你有多少种解锁方式走过了所有的0号点.
输入:包含多组实例.每个实例第一行为n和m,表示矩阵的大小,接下来是一个n*m的矩阵,包含数字0,1,2.对应上面的0号,1号,2号点.其中0号点的数目在[1,16]之间.
输出:不同解锁模式的总数.
分析:这道题目和TSP问题很类似,需要走完每个0号点,且每个0号点都只能被走1次.令d[i][S]=x表示当前在第i个0号点,且走过了集合S中的所有0号点时总共的行走方式数为x.
d[i][S]= sum{ d[j][S-{i}] } 如果在状态S-{i}下,从j到i之间不会经过一个1号点或没走过的0号点,那么可以转移状态.
初值:d[i][{i}]=1
复杂度分析16*(1<<16)=100W左右.
超时代码:测试结果正确但是超时.(未用long long表示d,会溢出)
#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>using namespace std; //下面是二维几何************************************************const double eps=1e-10;struct Point{ double x; double y; Point(double _x=0,double _y=0){x=_x;y=_y;} Point operator +(const Point &b)const { return Point(x+b.x,y+b.y); } Point operator -(const Point &b)const { return Point(x-b.x,y-b.y); } Point operator *(double p)const { return Point(x*p,y*p); } Point operator /(double p)const { return Point(x/p,y/p); }};typedef Point Vector;int dcmp(double x){ if(fabs(x)<eps) return 0; else return x<0?-1:1;}bool operator == (const Point& a,constPoint &b){ return dcmp(a.x-b.x)==0 && dcmp(a.y-b.y)==0;}double Dot(Vector A,Vector B){ returnA.x*B.x + A.y*B.y;}double Length(Vector A){ return sqrt(Dot(A,A));}double Angle(Vector A,Vector B){ return acos( Dot(A,B)/Length(A)/Length(B) );}double Cross(Vector A,Vector B){ return A.x*B.y-A.y*B.x;}double Area2(Point A,Point B,Point C){ return Cross(B-A,C-A);}bool OnSegment(Point p,Point a1,Point a2){ return dcmp( Cross(a1-p,a2-p) )==0 && dcmp( Dot(a1-p,a2-p))<0;}//上面是二维几何************************************************ int n,m;int d[16][1<<16];int g[10][10];//保存初始矩阵int cnt;//共有cnt个0号点int x[20],y[20];//x[i]和y[i]表示第i个0号点的x与y坐标int get_id(int px,int py)//得到对应0号点的编号{ for(int i=0;i<cnt;i++) if(x[i]==px&&y[i]==py) return i; return -100000;}bool has_way(int j,int i,int S1)//判断在状态S1下从j到i之间是否有路{ //即j到i之间是否有1号点或没走过的0号点 for(int px=0;px<n;px++) for(int py=0;py<m;py++) { if(g[px][py]==2)//可以滑过不可走上点 continue; else if(g[px][py]==0)//普通点 { int k = get_id(px,py); if(k==i || k==j)continue; if(S1&(1<<k))//第k个0号点已经被走过 continue; } double p1,p2,a1,a2,b1,b2;//判断当前点是否在线段i和j之间 p1=(double)px; p2=(double)py; a1=(double)x[i]; a2=(double)y[i]; b1=(double)x[j]; b2=(double)y[j]; Point P(p1,p2),A(a1,a2),B(b1,b2); if(OnSegment(P,B,A)) return false; } return true;}int dp(int i,int S){ if(d[i][S]>=0)return d[i][S]; int &ans = d[i][S]; int S1 = S^(1<<i); ans = 0; for(int j=0;j<cnt;j++) if(j!=i&& S1&(1<<j) && has_way(j,i,S1)) ans += dp(j,S1); return ans;}int main(){ while(scanf("%d%d",&n,&m)==2) { cnt= 0; for(int i=0;i<n;i++) for(int j=0;j<m;j++) { scanf("%d",&g[i][j]); if(g[i][j]==0) { x[cnt]=i; y[cnt++]=j; } } memset(d,-1,sizeof(d));//非法状态无法到达,直接初始为-1 for(int i=0;i<cnt;i++) d[i][1<<i]=1; int sum=0; int all=(1<<cnt)-1; for(int i=0;i<cnt;i++) sum += dp(i,all); printf("%d\n",sum); } return 0;}
上面代码超时的关键在于判断在状态S1下i与j之间是否有一条路?我上面用的方法是25个点,依次用计算机几何的方法判断任意一个不可跨过的点是否在线段i-j之间.然后就超时了.
解法二:其实这里面每个点的坐标都是在一个矩阵中的,判断从i到j有没有其他点,只要从i向j移动即可,每次移动一小步,最多移动4步(想想为什么?).如果移动到了一个非法点上,那么i与j就没有路,否则就有路.
那么什么叫每次移动一小步呢?假设i为(0,0),j为(2,4),那么从i到j的向量是(2,4),2和4不是一小步,因为2和4还可以同比例分解成更小的整数1和2(可以看出2和4的最大公约数为2),所以1和2才是一小步.x=x+1,y=y+2.
所以我们求出i到j的向量后只要求dx与dy的最大公约数就可以知道一小步是多少,然后最多走4步不越界即可,判断每一步是否合法.
AC代码:
#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int n,m;long long d[16][1<<16];int g[10][10];//保存初始矩阵int cnt;//共有cnt个0号点int x[20],y[20];//x[i]和y[i]表示第i个0号点的x与y坐标int gd[6][6];int mp[20][20];//mp[i][j]=x表示(i,j)格是第x个0号点int gcd(int a,int b)//最大公约数{ if(b==0)return a; return gcd(b,a%b);}bool has_way(int j,int i,int S1)//判断在状态S1下从j到i之间是否有路{ //即j到i之间是否有1号点或没走过的0号点 int x1=x[i],y1=y[i],x2=x[j],y2=y[j]; int dx = x1-x2,dy=y1-y2;//从1到2的绝对移动距离 int d = gd[abs(dx)][abs(dy)]; dx=dx/d;//一小步的x位移量 dy=dy/d;//一小步的y位移量 for(int step=1;step<=4;step++)//最多走4步 { int x3 =x2 + step*dx; int y3 =y2 + step*dy; if(x3==x1&&y3==y1)break;//从(x2,y2)点到达了(x1,y1)点 if(g[x3][y3]==0)//0号点 { int k = mp[x3][y3]; if( !(S1&(1<<k)) )//S1不包含k,即k是为走过的0号点 return false; } else if(g[x3][y3]==1)//1号点不能走 { return false; } } return true;}long long dp(int i,int S){ if(d[i][S]>=0)return d[i][S]; long long &ans = d[i][S]; int S1 = S^(1<<i); ans = 0; for(int j=0;j<cnt;j++) if(j!=i&& S1&(1<<j) && has_way(j,i,S1)) ans += dp(j,S1); return ans;}int main(){ for(int i=0;i<6;i++) for(int j=i;j<6;j++) gd[i][j]=gd[j][i]=gcd(i,j); while(scanf("%d%d",&n,&m)==2) { cnt= 0; for(int i=0;i<n;i++) for(int j=0;j<m;j++) { scanf("%d",&g[i][j]); if(g[i][j]==0) { x[cnt]=i; y[cnt++]=j; mp[i][j]=cnt-1;//坐标->0号点编号 的映射 } } memset(d,-1,sizeof(d));//非法状态无法到达,直接初始为-1 for(int i=0;i<cnt;i++) d[i][1<<i]=1; long long sum=0; int all=(1<<cnt)-1; for(int i=0;i<cnt;i++) sum += dp(i,all); printf("%I64d\n",sum); } return 0;}
0 0
- HDU4026
- 上海站1006 hdu4026
- 1000. 用指针方法处理数的排序
- LeetCode---Maximum Subarray
- GSM MAP Forward SMS Signaling Introduction
- 成大事必备9种能力、9种手段、9种心态
- 不使用别名的两种方法
- HDU4026
- Android Bitmap的缓存实现再也不怕ListView多图 oom了
- 安卓错误:android.widget.TextView.setText(TextView.java:3473)
- mysql5.5.29绿色版的安装
- 九度1139解题报告
- mysql5.5.29绿色版的安装
- 替换空格
- poj(3962)最大团
- 内存管理(1)物理内存描述