log翻硬币

若果有一组硬币,(假定有十个),每一个硬币仅仅有两个面,正面用以表示。反面用零表示.

给定目标(初始状态)1111100000 正正正正正反反反反反

                  (目标状态)   1000011101 正反反反反正正正反正

规定每次仅仅能够翻转相邻的两个硬币(他们各自成为原来的对立面)

问题,至少用多少次就能够达到目的.

将每一种状态标记为,每一次的硬币翻动(1-9)状态i。都会使。当前状态变为还有一种状态状态i+1,假设在状态

i+1的位置,翻动和对应的位置,则会,回到状态i,因此推断是(状态与状态之间的转换)能够互相进行.以此推断图的搜索.

此处採索,

方法一

分析.

每一个硬币翻转后都会(且必须)影响一硬币,硬币个数大于一

考虑每次翻转都会有两个硬币翻转到相反状态,假设目标与初始有基数个不同样。就不能达到目标状态.

我们尽可能的是硬币当前硬币接近给定状态

因此从左向右,假设i号硬币与目标同样,跳过。否则翻转i,i+1号硬币.(i<n),翻转次数加一.

当到i==n是假设i号与目标同样则得到翻转次数.

否则不能通过有该翻转规则得到,由于有一个不同(1。是基数而该规则是,每次改变两个硬币).

问题是怎样证明最有性.

一个硬币假设被翻转偶数次相当与没有翻转

若被翻转多次,如果硬币i被翻转n次,相当于n%2次

那麽i-1,被翻转x次(相当于x%2),i+1被翻转了n-x次((n-x)%2)

x为偶数。n-x为奇数 n为奇数 翻转i和i+1

x为偶数 n-x为偶数 n也为偶数 相当于没有不论什么硬币被翻转

x为奇数 n-x为偶数 n为基数      相当于值翻转i-1,i

x为基数n-x为基数翻转了i-1,i+1.

通过上述四种情况我们发现能够对一个硬币有两种操作

1.翻转左右各一次(多次效果同样次数增多不是问题的解)使得相距一个的硬币翻转

2.翻转左边一次或者右边一次,效果都是使得相邻的硬币发生翻转

以以下情况举例说明2覆盖1

a.假如 要是0 0 0 翻转到 1 0 1

第一种翻转方法翻转两边使得达到结果次数为2

另外一种方法翻转1,2,使得达到效果结果次数为2

b.要是0 0 0 变为 1 1 0

第一种方式不能达到,而另外一种方式仅仅要翻转1。影响2(翻转2,影响1)就可以结果为1

由情况a,b能够看出要改变一个硬币仅仅需改变他和他相邻的硬币就可以.

相邻的有两边。那麽要改变硬币i,究竟要硬向那边,能够从第一个開始来推断是否i号须要被改变,那麽左边的都是完毕改变的,因此仅仅需顾虑右边就好了.

那可能会有疑问了,会不会从右边開始结果更少呢?

从两边開始结果一样的

非常明确的知道。全部翻转的点是与目标不同的点.如今有m个(m是偶数)由于已经证明结论基数个不同样硬币是不可能相邻翻转来改变是指同样。此时设有0<=i<j<=n;

a[i],a[j]都与目标不同样,此时从左向右还是从右翻转都是j-i次,那麽原硬币中有m个与终于结果不同样,此时可将这m开做k(o..m/2)的ik..jk.不同样的亮亮对,他们翻转操作次数

都是k(1...m/2) 求和ij-ik,因此偶数(可通过翻转到达的情况是从左到右从右到左是同样的).

比如记i号硬币起始情况与终于情况同样为1,否则为零那麽对以下的情况用上面的结论来计算结果.

1 0 1 0 0 0 1 0 1 0 

从左向右(4-2)+ (6-5)+ (10 -8)= 5次

从右向左 (10-8) + (6-5) + (4-2) =5次

上面规律应征名,全部这杨m为偶数结果,从左向右,从右向左都同样。

以下解法即从左向右.

#include <stdio.h>
int main(){
        int count=0,i;
        int a[10];
        int b[10];
        for(i=0;i<10;i++){
                scanf("%d",&a[i]);
        }
        for(i=0;i<10;i++){
                scanf("%d",&b[i]);
        }
        for(i=0;i<9;i++){
                if(a[i]!=b[i]){
                        a[i]=!a[i];
                        a[i+1]=!a[i+1];
                        count++;
                }
        }
        printf("%d
",a[i]==b[i]?

count:-1); return 0; }


方法二

採取bfs,搜素状态树就可以.

要点两个:

1.状态的保存(判反复节点).若果节点反复则不应打开(去遍历他所相应的节点间)。

2.状态的表示,该题目的状态的表示,使用简单的哈希相应关系.

#include <iostream>
#include <cstring>
using namespace std;
const int size = 1000000+100;  
typedef int state[10];
state st[size];
int vis[size]={0};   
int dis[size]={0};   
int getkey(state &s)
{
 int i,adder=0;
 for(i=0;i<10;i++){
   adder=adder*2+s[i];
  }
 return adder;
}
int isvis(state &s){
 int key=getkey(s);
 if(vis[key])
 return 1;
 else {
   vis[key]=1;
   return 0;
  }
}
int bfs(int front,int rear,state goal)
{
 int i,j,k;
 while(front!=rear){
   state &s=st[front];
   if(memcmp(goal,s,sizeof(s))==0)
   return dis[front];
   else{
      for(i=0;i<9;i++){
          state &t=st[rear];
          
          memcpy(&t,&s,sizeof(s));
          t[i]=!t[i];
          t[i+1]=!t[i+1];
          dis[rear]=dis[front]+1;
          if(!isvis(t))
          rear++; 
         }
     }
   front++;
  }
 return -1;
}
int main()
{
 int front=1,rear=2,i,j,k;
 state goal;
 int distance=-1;
 for(i=0;i<10;i++){
   cin>>st[front][i];
  }
 for(i=0;i<10;i++){
   cin>>goal[i];
  }
 distance=bfs(front,rear,goal);
 cout<<distance<<endl;
 return 0;
}
给出10个硬币初始状态,和终于状态得到最短反转步数.

原文地址:https://www.cnblogs.com/mfrbuaa/p/5159959.html