NKOI 3744 智力游戏
2016-08-27 19:55
232 查看
P3744智力游戏
问题描述小A在玩一个简单的智力游戏,这个游戏是这样的:
在一个4*4的矩阵中分别有4个1,4个2,4个3和4个4。
每一步小A可以把同一行的4个数往左移或者往右移一步或者把同一列的4个数字往上移或者往下移一步(1,2,3,4往左移后是2,3,4,1)。
小A现在想知道最少几步移动可以使得矩阵的每行上的4个数字都一样或者每列上的4个数字都一样。但是小A又不想走太多步,他只要知道最少步数是否少于等于5步,是的话输出准确的步数,否则输出-1。
输入格式
一个4*4的矩阵
输出格式
一个整数,表示最优步数。无解输出-1
样例输入 1
1 2 3 4
1 2 3 4
1 2 3 4
2 3 4 1
样例输出 1
1
样例输入 2
4 1 1 1
1 2 2 2
2 3 3 3
3 4 4 4
样例输出 2
1
本题要用到迭代加深dfs+启发式搜索,重点在于估价函数的设计
我们设定估价函数: F(i)=G(i)+H(i) G(i)记录到达i棋盘状态所需步数 H(i)=min{ 所有行最少所需操作的次数总和,所有列最少所需操作的次数总和 }
这里的最少操作次数是对于单个一行/列来讲的,即这一行/列最少保证到达目标状态的操作次数
我们讨论将下面棋盘转换成合法目标棋盘需要的操作数:
1 1 3 2
2 4 4 4
3 3 1 2
1 2 3 4
水平方向:第1行至少2次,第2行至少1次,第3行至少2次,第4行至少3次,总次数至少8次
垂直方向:第1列至少2次,第2列至少3次,第3列至少2次,第4列至少2次,总次数至少9次
1 2 3 4
1 2 3 4
1 2 3 4
2 3 4 1
如上样例数据所示,水平方向总次数为3+3+3+3=12,垂直方向总次数为1+1+1+1=4 看起来调整垂直方向更合算。于是,我们将第四行右移一位,发现1次操作达到的最终目的。 而我们用上面方法估算出的次数至少是4。 矛盾的原因是,在理想情况下,我们通过一次移动,可以使得4个数字都到达合适的位置上。
于是,我们修改估价函数:
H(i)=(min{ 所有行最少所需操作的次数总和,所有列最少所需操作的次数总和 }+3)/4
这里+3即为向上取整
#include<cstdio> #include<iostream> #include<cstring> using namespace std; int s[10][10],depth=1; int geth(){ int temp1=0,temp2=0,i,j; int cnt[10]; for(i=1;i<=4;i++){ int maxx=0; memset(cnt,0,sizeof(cnt)); for(j=1;j<=4;j++){ cnt[s[i][j]]++; maxx=max(cnt[s[i][j]],maxx); } temp1+=4-maxx; } for(j=1;j<=4;j++){ int maxx=0; memset(cnt,0,sizeof(cnt)); for(i=1;i<=4;i++){ cnt[s[i][j]]++; maxx=max(cnt[s[i][j]],maxx); } temp2+=4-maxx; } return (min(temp1,temp2)+3)/4; } void move_right(int x){ int i,y=s[x][4]; for(i=4;i>1;i--)s[x][i]=s[x][i-1]; s[x][1]=y; } void move_left(int x){ int i,y=s[x][1]; for(i=1;i<=3;i++)s[x][i]=s[x][i+1]; s[x][4]=y; } void move_up(int x){ int i,y=s[1][x]; for(i=1;i<=3;i++)s[i][x]=s[i+1][x]; s[4][x]=y; } void move_down(int x){ int i,y=s[4][x]; for(i=4;i>1;i--)s[i][x]=s[i-1][x]; s[1][x]=y; } bool dfs(int k){ int h=geth(); if(k+h>depth)return 0; if(h==0)return 1; int i; for(i=1;i<=4;i++){ move_left(i); if(dfs(k+1))return 1; move_right(i); move_right(i); if(dfs(k+1))return 1; move_left(i); move_up(i); if(dfs(k+1))return 1; move_down(i); move_down(i); if(dfs(k+1))return 1; move_up(i); } return 0; } int main(){ int i,j; for(i=1;i<=4;i++) for(j=1;j<=4;j++)cin>>s[i][j]; if(!geth()){ puts("0"); return 0; } for(depth=1;depth<=5;depth++) if(dfs(0))break; printf("%d",depth<=5?depth:-1); }
相关文章推荐
- 代码的鲁棒性:反转链表
- JVM的数据类型
- 我对javaFx的简单认识
- UCI机器学习库和一些相关算法
- 内核中操作寄存器的方法
- Linux学习-性能监控
- Spring IOC容器中注入bean
- liuzhongsky Dolph于其他博客中存在的博文链接汇总
- http://edu.qq.com/a/20151012/055363.htm
- CSU - 1753 野心
- CCNA学习笔记1--ip地址分类和水晶头线序和设备互联法则
- Contains Duplicate II
- web浏览器工作原理
- Material风格的确认框
- android开发/设计原则总结
- 粒子群算法的几个适应度评价函数
- opencv学习笔记---1
- java多线程相关内容
- 曾经做过的c练习(1-5)
- 装逼神器shellinabox基于WEB远程终端模拟器