上海站1006 hdu4026

来源:互联网 发布:淘宝网吴清源对局全集 编辑:程序博客网 时间:2024/05/16 18:16
/*题意:n行,每行n个点,规则排列。每个点有属性,0表示平常点,1表示坏点,既不能触碰,也不能从它上面飞过      2表示不能触碰,但可以从它上面飞过。现在要求有且一次经过所有平常点的路径数(一平常点到另一平常点  的直线路径中不能有坏点和平常点;平常点被触碰后变成2类点)分析:因为n<=5,并且平常点的个数小于16.所以把平常点标号,然后初始化任意两点平常点直接可达所需的条件      dp[i][j]表示现在在第i个平常点上,并且前面已走平常点的二进制表示为j的方法数*/#include<stdio.h>#include<iostream>using namespace std;struct point{int x,y;}p[20];int sum,n,m,num1,f[10][10],flag[30][30];__int64 num,dp[16][1<<16];int Min(int a,int b){return a<b? a:b;}int Max(int a,int b){return a>b? a:b;}__int64 DFS(int now,int bit){if(dp[now][bit]!=-1) return dp[now][bit];if(bit==sum)//平常点全部走完了return dp[now][bit]=1;int i;__int64 sum=0;for(i=0;i<num1;i++)if((flag[now][i]|bit)==bit&&(bit|(1<<i))!=bit)//可直接到达并且以前没到达过sum+=DFS(i,bit+(1<<i));return dp[now][bit]=sum;}int main(){int i,j,ii,jj,k,kk,iii,jjj,kkk;while(scanf("%d%d",&n,&m)!=EOF){for(num1=0,i=0;i<n;i++)for(j=0;j<m;j++){scanf("%d",&f[i][j]);if(!f[i][j])p[num1].x=i,p[num1++].y=j;}sum=(1<<num1)-1;//所有平常点的二进制表示memset(flag,0,sizeof(flag));for(iii=0;iii<num1;iii++)//第iii个平常点到第jjj个平常点所需的条件for(jjj=0;jjj<num1;jjj++){i=p[iii].x,j=p[iii].y;ii=p[jjj].x,jj=p[jjj].y;if(i==ii&&j==jj) continue;//同一个点if(ii==i)//在同一行{for(k=Min(j,jj)+1;k<Max(j,jj);k++)//枚举列的坐标{if(flag[iii][jjj]==-1) break;//中间有坏点,永远不可能直接到达if(f[ii][k]==2) continue;if(f[ii][k]==1){flag[iii][jjj]=-1;break;}for(kkk=0;kkk<num1;kkk++)if(p[kkk].x==ii&&p[kkk].y==k)break;flag[iii][jjj]+=(1<<kkk);//直接到达,这个平常点必须要先遍历过}}else{for(k=Min(i,ii)+1;k<Max(i,ii);k++)//枚举横坐标{if(flag[iii][jjj]==-1) break;if(((ii-k)*(jj-j))%(ii-i)!=0) continue;kk=jj-((ii-k)*(jj-j))/(ii-i);//枚举纵坐标if(f[k][kk]==2) continue;if(f[k][kk]==1){flag[iii][jjj]=-1;break;}for(kkk=0;kkk<num1;kkk++)if(p[kkk].x==k&&p[kkk].y==kk)break;flag[iii][jjj]+=(1<<kkk);}}}memset(dp,-1,sizeof(dp));for(num=0,i=0;i<num1;i++)num+=DFS(i,1<<i);printf("%I64d\n",num);}return 0;}

 
原创粉丝点击