openjudge-拨钟问题

描述

有9个时钟,排成一个3*3的矩阵。

|——-| |——-| |——-|
| | | | | | |
|—O | |—O | | O |
| | | | | |
|——-| |——-| |——-|
A B C

|——-| |——-| |——-|
| | | | | |
| O | | O | | O |
| | | | | | | | |
|——-| |——-| |——-|
D E F

|——-| |——-| |——-|
| | | | | |
| O | | O—| | O |
| | | | | | | |
|——-| |——-| |——-|
G H I
(图 1)
现在需要用最少的移动,将9个时钟的指针都拨到12点的位置。共允许有9种不同的移动。如下表所示,每个移动会将若干个时钟的指针沿顺时针方向拨动90度。

移动 影响的时钟

1 ABDE
2 ABC
3 BCEF
4 ADG
5 BDEFH
6 CFI
7 DEGH
8 GHI
9 EFHI

输入

9个整数,表示各时钟指针的起始位置,相邻两个整数之间用单个空格隔开。其中,0=12点、1=3点、2=6点、3=9点。

输出

输出一个最短的移动序列,使得9个时钟的指针都指向12点。按照移动的序号从小到大输出结果。相邻两个整数之间用单个空格隔开。

样例输入

3 3 0
2 2 2
2 1 2

样例输出

4 5 8 9

来源

1166

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;

unsigned int clocks[10]; 
    //保存最后最少的移动次数,最多也就是27次
int min_res = 28, tmp = 0;
    //保存最小次数时候的移动方法
unsigned int min_op[10] = {0}, op[10] = {0};

void isOk(const unsigned int i1,const unsigned int i2,const unsigned int i3);//如果验证成功 那么返回1  否则返回0  

void enumerate()//将前3行的拨钟数目进行枚举 返回枚举方法 循环写在这里面  
{  
    for(unsigned int i=0;i<4;i++)  
    {   
        for(unsigned int j=0;j<4;j++)  
        {   
            for(unsigned int k=0;k<4;k++)
            {  
               isOk(i,j,k);  
            }    
        }  
    }   
}  
/* 

 1        ABDE  
 2        ABC  
 3        BCEF  
 4        ADG  
 5        BDEFH  
 6        CFI  
 7        DEGH  
 8        GHI  
 9        EFHI          
*/  
void isOk(const unsigned  int i1,const unsigned int i2 ,const unsigned int i3)//如果验证成功 那么返回1  否则返回0  
{  
    op[1]=i1;
    op[2]=i2;
    op[3]=i3;
    op[4]=(4-(clocks[1]+op[1]+op[2])%4)%4;//确定A 
    op[5]=(4-(clocks[2]+op[1]+op[2]+op[3])%4)%4;//确定B  
    op[6]=(4-(clocks[3]+op[2]+op[3])%4)%4; //确定C  
    op[7]=(4-(clocks[4]+op[1]+op[4]+op[5])%4)%4;//确定D  
    op[8]=(4-(clocks[7]+op[4]+op[7])%4)%4;//确定G  
    op[9]=(4-(clocks[5]+op[1]+op[3]+op[5]+op[7])%4)%4;//确定E  
    //f  h  i  
    if((clocks[6]+op[3]+op[5]+op[6]+op[9])%4==0  && (clocks[8]+op[5]+op[7]+op[8]+op[9])%4==0 
        && (clocks[9]+op[6]+op[8]+op[9])%4==0)//满足条件  
    {  

        tmp = 0;
        for (int j = 1; j <= 9; j++)
        tmp += op[j];
        if (tmp < min_res)//找到了更优秀的解
        {
            min_res = tmp;
            memcpy(min_op, op, sizeof(unsigned int)*10); 
            return ;  
        }  
        else//不符合要求 直接return继续寻找  
        {  
            return ;  
        }  
    }  
    return ;  
}  
int main()
{
    int tag0=0;
    for (int i = 1; i <= 9; i++)
        cin >> clocks[i];
    //非零项排序
    enumerate();
    vector<unsigned int> res;
    for (int i = 1; i <= 9; i++)
    {
        if (min_op[i] != 0)
        {
            tag0=1;
            res.push_back(i);
            min_op[i]--;
            i--;
        }
    }
    sort(res.begin(), res.end());
    if(tag0)
    {
    /**/
        for (vector<unsigned int>::iterator it = res.begin(); it != res.end(); it++)
            cout << *it << ' ';
    }
    else    cout << "0";
    cout <<endl;
    return 0;
 } 

解题思路

/*这一题实质类似熄灯问题和画家问题。其共通点在于:

操作对环境的改变是无序的,每个操作都会影响到周围的状态。

同时每一种操作都有周期性限制,也即最多需要几次操作,多于这个次数产生循环。

熄灯问题中,每个灯最多熄灯一次,因为灯只有两种状态,
并且循环。而这里,有4种循环的状态,因此每个移动操作顶多使用3次。

我们对移动方法1,2,3进行枚举,每种方法无非实施0-3次,也即一共4^3=64种情况。
这些情况之间并非没有关系。

例如,我们确定了1,2,3的情况数,那么得到一个灯A,B,C的状态,而只有移动4能够改变A,
移动5能够改变B,移动6能够改变C,那么移动4-6的次数也确定了。

同样,这时只有移动7能够改变D,移动9能够改变F,这时移动7和9的次数也确定了。

最后,时钟A,B,C,D,F都已经到达12点,E,G,H,I还没确定,只剩下移动8能够改变GHI,
所以只要检查E是否已经到达12点以及,GHI的时钟数是否相等就行了。

最后找到一个移动次数最小的情况。

这题也可以用暴力搜索,因为最多有4^9个组合,不会超时。

这题还可以列出一个方程组,九个未知数,通过高斯消元法来解方程组。*/
//思路引用于:http://www.cnblogs.com/CCBB/archive/2011/09/13/2175043.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
openjudge约瑟夫问题是一个数学应用问题问题的描述是:有n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。最后剩下的那个人就是猴王的编号。 这个问题在openjudge中的输入格式是每行是用空格分开的两个整数,第一个是n, 第二个是m (0 < m,n <= 300)。最后一行是0 0作为输入的结束标志。 要解决openjudge约瑟夫问题,我们需要编写一个程序来接收输入的n和m值,并按照约瑟夫问题的规则计算出最后剩下的猴王的编号。具体的解题算法可以使用循环队列或者递归的方式来实现,具体实现的代码可以根据具体的编程语言来完成。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [openjudge 约瑟夫环问题](https://blog.csdn.net/sdz20172133/article/details/88351098)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [bailian.openjudge 2746:约瑟夫问题](https://blog.csdn.net/smartzxf/article/details/100829938)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值