zoukankan      html  css  js  c++  java
  • POJ1184Smart typist

    转载请注明出处:優YoU  http://user.qzone.qq.com/289065406/blog/1311864665

    大致题意:

    l       通过给定的六种操作将一个六位数变为另一个六位数,求需要的最少操作数。

    l       六种操作:

    l       左移和右移:将光标位置左移一位或右移一位,在第一位时无法左移,最后一位时无法右移。

    l       左交换和右交换:将光标位置的数字与第一位或最后一位交换

    l       增大或减小:将光标位置的数字增大或减小1

     

    解题思路:

    BFS+状态压缩

     

    初步想法

    l       很难找到有效的贪心算法

    l       没有明显的局部最优特性,无法动态规划

    l       考虑搜索

    直观的想法

    l       直接进行搜索,从初态开始,知道找到末态的最优解为止。

    l       无论空间,时间都行不通

    l       6个位置×1000000个不同的数=6000000个状态

    l       必须减少状态数

    两种操作的分离

    l       这六种操作对一个数有两种影响,一种是交换两个数位的位置,另一种是改变某个数位的值。

    l       当且仅当光标到达某一数位,对这一数位的值的改变才可能发生,而且其发生的时间并不重要。

    l       所以全部操作可分为两种:一种是移位和交换操作,一种是增大和减小操作。

    l       将操作分离成:先对原数的各数位重新排列(利用移位和交换操作),然后对光标到达过的位置进行增大或减小。

    问题转化:

    l       1.对每一种排列和光标到达情况,求出最少需要的操作数。(此过程与输入无关)

    l       2.求出在每一种排列下,需要的增大和减小操作的次数。(要求所有需要改变值的数位均被访问过)

    解题第一步

    l       状态数:

    l       6个位置×720种排列情况×26种光标访问情况

    l       进一步缩小状态数:

    l       因为光标是连续移动的,所以除了第6位以外,假如某一位被访问过,则它之前的数位均被访问过。第6位可用右交换操作访问,不在此列

    l       由此得到十种光标访问情况:

    l       1 被访问过

    l       1,2 被访问过

    l       1,2,3 被访问过

    l       1,2,3,4 被访问过

    l       1,2,3,4,5 被访问过

    l       1,6 被访问过

    l       1,2,6 被访问过

    l       1,2,3,6 被访问过

    l       1,2,3,4,6 被访问过

    l       1,2,3,4,5,6 被访问过

    l       现在状态数为6×720×10,可以接受了

    l       搜索方法:

    l       对左移,右移,左交换,右交换四种操作进行搜索。其中不难发现左移操作是多余操作,因为可以先改变数字大小再右移或者交换

    l       无法预知搜索深度,最优解多在浅层获得,故采用广度优先算法

    解题第二步

    l       对所有满足要求的情况(即需要改变大小的数位光标都访问过),找出需要总操作最少的,输出。

     

     

      1 //Memory Time 
    2 //3000K 0MS
    3
    4 #include<iostream>
    5 #include<queue>
    6 #include<cmath>
    7 using namespace std;
    8
    9 class oper //记录每步操作情况(只针对移位和换位操作,并不包括值大小的改变操作),得到所有排列的状态
    10 {
    11 public:
    12 int num[6]; //第step步操作后的 "数字串"
    13 int state; //第step步操作后对 "数字串各个位置的访问状态" ,其值对应VistState的行数,分别为0~9
    14 int pos; //第step步操作后 "光标在数字串中的位置" ,分别为0~5
    15 int step; //当前操作的 "步数"
    16 };
    17
    18 int VistState[10][6]= /*访问状态数组,通过swap0、swap1与右移操作得到的"数字串各个位置的访问状态"*/
    19 { /*一行代表一种访问状态,1表示数字串该位置被访问了,0表示没有访问*/
    20
    21 1,0,0,0,0,0, /*访问状态0: 初始状态(pos=0)*/
    22 1,1,0,0,0,0, /*访问状态1: 状态0通过右移操作得到(pos=1),或者状态1通过swap0操作得到(pos=1)*/
    23 1,1,1,0,0,0, /*访问状态2: 状态1通过右移操作得到(pos=2),或者状态2通过swap0操作得到(pos=2)*/
    24 1,1,1,1,0,0, /*访问状态3: 状态2通过右移操作得到(pos=3),或者状态3通过swap0操作得到(pos=3)*/
    25 1,1,1,1,1,0, /*访问状态4: 状态3通过右移操作得到(pos=4),或者状态4通过swap0操作得到(pos=4)*/
    26 1,0,0,0,0,1, /*访问状态5: 状态0通过swap1操作得到(pos=0),或者状态5通过swap0操作得到(pos=0)*/
    27 1,1,0,0,0,1, /*访问状态6: 状态1通过swap1操作得到(pos=1),或者状态5通过右移操作得到(pos=1),或者状态6通过swap0操作得到(pos=1)*/
    28 1,1,1,0,0,1, /*访问状态7: 状态2通过swap1操作得到(pos=2),或者状态6通过右移操作得到(pos=2),或者状态7通过swap0操作得到(pos=2)*/
    29 1,1,1,1,0,1, /*访问状态8: 状态3通过swap1操作得到(pos=3),或者状态7通过右移操作得到(pos=3),或者状态8通过swap0操作得到(pos=3)*/
    30 1,1,1,1,1,1 /*访问状态9: 状态4通过swap1操作得到(pos=4),或者状态8通过右移操作得到(pos=4),或者状态9通过右移操作得到(pos=5),
    31 或者状态4通过右移操作得到(pos=5),或者状态9通过swap0操作得到,或者状态9通过swap1操作得到*/
    32 };
    33 /*不难注意到:swap0操作前后,光标位置pos不变,访问状态不变 ; swap0操作前后,pos不变;
    34 右移操作后,pos+1 ; 无需左移操作 */
    35
    36 int comb[720][8]; //记录某个数字串num的全部排列组合情况(各个数字值不变,位置不同),共6!=720种
    37 //comb[][0~5]=num[0~5], comb[][6]=state , comb[][7]=step
    38 int idx=0; //comb的行索引
    39 bool vist[6][6][6][6][6][6][6][10]; //标记出现过的状态,前6维为数字串num[],第7维为光标所在的位置pos,第8维为访问状态state
    40
    41 void BFS(void); //搜索所有"通过位移和换位操作"得到的排列组合状态
    42 bool CheckVist(oper* a); //状态检查
    43 void ChangeVist(oper* a); //状态变更
    44
    45 int main(void)
    46 {
    47 memset(vist,false,sizeof(vist));
    48 BFS(); //预处理: 对每一种排列和光标到达情况,求出最少需要"移位和换位的操作数"。(此过程与输入无关)
    49
    50 char Init_ANum[6]; //初始字符串
    51 int Init_DNum[6]; //初始数字串
    52 char Aim_ANum[6]; //目标字符串
    53 int Aim_DNum[6]; //目标数字串
    54
    55 while(cin>>Init_ANum>>Aim_ANum)
    56 {
    57 for(int k=0;k<6;k++) //字符串转换为数字串
    58 {
    59 Init_DNum[k]=Init_ANum[k]-'0';
    60 Aim_DNum[k]=Aim_ANum[k]-'0';
    61 }
    62
    63 int MinOper=1000000; //从str得到aim最少需要的操作数
    64 for(int i=0;i<idx;i++)
    65 {
    66 int cnt=comb[i][7]; //comb[i][7]=step,为移位和换位的总操作数
    67 bool flag=true;
    68 for(int j=0;j<6;j++)
    69 { //comb[i][6]=state
    70 if(!VistState[ comb[i][6] ][j] && (Init_DNum[ comb[i][j] ]!=Aim_DNum[j])) //str[]与aim[]在位置j的数字值不等,且该位置没有被访问过
    71 {
    72 flag=false; //comb[i]不符合要求
    73 break;
    74 }
    75 else
    76 cnt+=abs(Init_DNum[ comb[i][j] ] - Aim_DNum[j]); //在同一位置,值改变的次数(每一改变1)就是操作数
    77 }
    78
    79 if(flag)
    80 MinOper=MinOper<cnt?MinOper:cnt;
    81 }
    82
    83 cout<<MinOper<<endl;
    84 }
    85
    86 return 0;
    87 }
    88
    89 /*搜索所有"通过位移和换位操作"得到的排列组合状态*/
    90 void BFS(void)
    91 {
    92 oper a,b;
    93 queue<oper>Q;
    94
    95 for(int i=0;i<6;i++)
    96 a.num[i]=i;
    97 a.pos=a.state=a.step=0;
    98
    99 Q.push(a); //入队
    100 ChangeVist(&a);
    101
    102 while(!Q.empty())
    103 {
    104 a=Q.front(); //取队尾
    105 Q.pop(); //队尾元素出队
    106
    107 /*记录所有组合情况*/
    108
    109 for(int k=0;k<6;k++)
    110 comb[idx][k]=a.num[k];
    111 comb[idx][6]=a.state;
    112 comb[idx++][7]=a.step;
    113
    114 if(a.pos>0) //swap0操作前提条件,注意swap0操作前后的访问状态不变,光标位置也不变
    115 { //因此无需处理b.state与b.pos
    116
    117 /*swap0操作*/
    118
    119 b=a; //备份
    120 b.step=a.step+1;
    121 swap(b.num[0],b.num[b.pos]); //交换num第0位与第pos位
    122 if(!CheckVist(&b)) //状态检查
    123 {
    124 ChangeVist(&b);
    125 Q.push(b); //入队
    126 }
    127 }
    128
    129 if(a.pos<5) //右移right或swap1操作前提条件
    130 {
    131 /*right操作,注意光标位置pos会改变*/
    132
    133 b=a;
    134 b.step=a.step+1;
    135 b.pos++;
    136 if(b.state<9)
    137 b.state++;
    138
    139 if(!CheckVist(&b)) //状态检查
    140 {
    141 ChangeVist(&b);
    142 Q.push(b); //入队
    143 }
    144
    145 /*swap1操作,注意光标位置pos不变*/
    146
    147 b=a;
    148 b.step=a.step+1;
    149 swap(b.num[5],b.num[b.pos]); //交换num第5位与第pos位
    150 if(b.state<5)
    151 b.state+=5;
    152
    153 if(!CheckVist(&b)) //状态检查
    154 {
    155 ChangeVist(&b);
    156 Q.push(b); //入队
    157 }
    158 }
    159 }
    160 return;
    161 }
    162
    163 /*状态检查*/
    164 bool CheckVist(oper* a)
    165 {
    166 int* p=a->num;
    167 return vist[*p][*(p+1)][*(p+2)][*(p+3)][*(p+4)][*(p+5)][a->pos][a->state];
    168 }
    169
    170 /*状态变更*/
    171 void ChangeVist(oper* a)
    172 {
    173 int* p=a->num;
    174 vist[*p][*(p+1)][*(p+2)][*(p+3)][*(p+4)][*(p+5)][a->pos][a->state]=true;
    175 return;
    176 }

  • 相关阅读:
    javafx DragDropped file
    javafx style and cssFile
    javafx ComboBox Event and change cell color
    javafx clipboard
    javafx Cursor
    javafx DropShadow
    javafx checkbox
    javafx image button
    GNS3连接虚拟机
    cain使用教程
  • 原文地址:https://www.cnblogs.com/lyy289065406/p/2122873.html
Copyright © 2011-2022 走看看