牛牛正在挑战一款名为01翻转的游戏。游戏初始有A个0,B个1,牛牛的目标就是把所有的值都变为1,每次操作牛牛可以任意选择恰好K个数字,并将这K个数字的值进行翻转(0变为1,1变为0)。牛牛如果使用最少的操作次数完成这个游戏就可以获得奖品,牛牛想知道最少的操作次数是多少?
例如:A = 4 B = 0 K = 3
0000 -> 1110 -> 1001 -> 0100 -> 1111
需要的最少操作次数为4
输入为一行: 一共三个整数A(0 ≤ A ≤ 100,000),B(0 ≤ B ≤ 100,000),K(1 ≤ K ≤100,000).以空格分隔
输出一个整数,表示最少需要的操作次数。如果不能完成,则输出-1
4 0 3
4
#include <iostream> using namespace std; int main() { int K,A,B, MAX = 200000; while(cin>>A>>B>>K) { int N; for (N = 0; N < MAX; N++) { if ((N*K - A) < 0 || ((N*K - A) & 0x1) != 0) continue; if ((N*K - A)/2 <= A*((N-1)/2) + B*(N/2) || A == 0) break; } if (N < MAX) cout<<N<<endl; else cout<<-1<<endl; } return 0; }
#include<iostream> #include<cmath> using namespace std; int function(int A, int B, int K){ int remainder = A % K;//直接翻转后的剩余待翻转个数 int count = A / K;//直接翻转 int S = A + B;//总个数 if (A == 0 || remainder == 0) ; else if ((S <= K) || (remainder % 2 == 1 && K % 2 == 0)) count = -1;//无法翻成功的输出-1 else if ((K + remainder) % 2 == 0 && count > 0 && B + K * count >= 2 * K - (remainder + K) / 2) count++;//一种特殊情况,还剩下K+remainder个0时直接翻两次即可完成 else if (remainder % 2 == 0) count += 2 * ceil(remainder / double(2 * (S - K))); //每翻两次最多能把remainder中的2*(S-K)个0翻成1,注意这里指的是最多,当翻最后2次或者S-K>remainder/2时,只需翻两次,所以这里用到了ceil() else count += 2 * ceil((K - remainder) / double(2 * (S - K))) + 1; //当remainder是奇数时,相当于先把所有1中的K-remainder个翻成0,这样加上remainder一共K个0,只需额外再翻一次即可,K-remainder是奇数时,永远不能翻成功,是偶数时,翻转方法同上面 return count; } int main(){ int A, B, K; cin >> A >> B >> K; cout << function(A, B, K); return 0; } //没有循环,只用到了条件语句,时间复杂度O(1),空间复杂度O(1)
import sys def main(A, B, K): if A == 0: return 0 if K == 0: return -1 if A % K == 0: return A / K if A & 1 > K & 1 or K >= A + B: return -1 ret = A // K + 1 while 1: if (ret * K - A) & 1 == 0 and ret * K <= ret * (A + B) - (A, B)[ret & 1]: return ret ret += 1 print main(*map(int, sys.stdin.readline().split()))
import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; /* * 问题:一行数字,初始有a个0和b个1,现在目标是把所有的值都变为1,每次可以任意选择恰好k个数字,并将这个k个数字 * 的值进行翻转(0变成1,1变成0)。请问达到目标最少需要几次操作。 */ /* * 有初始状态和结束状态,求最少操作次数。显然这是一个宽度优先遍历(BFS) * 初始状态:a个0,b个1 * 结束状态:0个0,(a+b)个1 * 边(规则):每次可以翻转k个数 */ public class FanZhuan { public static void main(String[] args) { Scanner scanner=new Scanner(System.in); int a=scanner.nextInt(); int b=scanner.nextInt(); int k=scanner.nextInt(); scanner.close(); int count=BFS(a, b, k); System.out.println(count); } public static int BFS(int a,int b,int k){ if(a==0 && b==0){ return 0; } if(k>a+b){ return -1; } Node node=new Node(a, b, 0);//初始状态 Queue<Node> queue=new LinkedList<>();//队列 boolean[] vis=new boolean[a+b+1];//判断是否出现过 Arrays.fill(vis, false);//全部初始化成false queue.add(node); vis[a]=true; while(!queue.isEmpty()){ Node temp=queue.poll(); if(temp.a==0){ return temp.step; } //选择k个数字进行反转,假设选择了i个0,i要小于等于a和k的最小值,i至少为1 //这时1翻转的次数就为k-i for(int i=1;i<=Math.min(temp.a, k);i++){ if(temp.b<k-i){//没有那么多1可供翻转,那就继续,多翻转一个0 continue; } int newa=temp.a-i+(k-i);//翻转i个0后,还剩余的0的个数。 if(newa==0){ return temp.step+1; } //判断newa个0的情况有没有出现过 if(!vis[newa]){ vis[newa]=true; queue.add(new Node(newa, a+b-newa, temp.step+1)); } } } return -1; } } class Node{ int a;//表示0的个数 int b;//表示1的个数 int step;//表示操作次数 public Node(int a,int b,int step) { this.a=a; this.b=b; this.step=step; } }
import java.util.Scanner; public class Main{ public static void main(String[] args) { Scanner sc = new Scanner(System.in); int A = sc.nextInt(); int B = sc.nextInt(); int K = sc.nextInt(); sc.close(); int g = A % K; if (g == 0) {//能直接整除 System.out.println(A / K); return; } if (A + B <= K) {//缺少交换空间 System.out.println("-1"); return; } if ((K % 2 == 0) && (g % 2 == 1)) {//剩余0的个数总为奇数,不可能有结果 System.out.println("-1"); return; } //下面分支对交换空间是否充足进行分析 //交换空间成立条件A + B - g >= K - g / 2(其中最差的情况就是g=2时) //说明只要在一次交换中尽可能减小g的值,就能最快的完成全部交换 if (g == A) { int count = 0; if (K % 2 == 0) {//如果K为偶数,则当前剩余0的个数一定是偶数 while (true) { if ((g % 2 == 0) && (A + B - g >= K - g / 2)) { count = count + 2; break; } if (count % 2 == 0) { count++; g = A + B - (K - (A + B - g)); } if (count % 2 == 1) { count++; g = K - g; } } } else { while (true) {//如果K为偶数,则当前剩余0的个数是一次奇数一次偶数的交替 if ((g % 2 == 0) && (A + B - g >= K - g / 2)) { count = count + 2; break; } if (g % 2 == 0) { count++; g = A + B - (K - (A + B - g)); } if (g % 2 == 1) { count++; g = K - g; } } } System.out.println(A / K + count); return; } if (A > 2 * K||B >(K+(g-A)/2)) { //举例A=10000 B=10000 K=548时,不需要贪心的交换到A剩余136的时候 //只需要交换到684的时候,进行两次交换即可全1,而不是贪心到底要多一次 System.out.println(A / K + 1); return; } if (g < A) if (g % 2 == 0) { System.out.println(A / K + 2); return; } else { System.out.println(A / K + 3); return; } } }
# coding=utf-8
"""
@author: beyourself
@time: 2018/5/28 9:58
@file: 01flip-flop.py
"""
A, B, K = [int(i) for i in input().split()]
MAX = 200000
for N in range(0, MAX + 2):
if (N * K - A < 0) or (((N * K - A) & 0x1) != 0):
continue
if ((N * K - A) // 2 <= A * ((N - 1) // 2) + B * (N // 2)) or (A == 0):
break
if N < MAX:
print(N)
else:
print('-1')
import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; class Step { public int a; public int b; public int step; public Step(int a, int b, int step) { super(); this.a = a; this.b = b; this.step = step; } } public class Main { private static Scanner in = new Scanner(System.in); public static void main(String[] args) { int a = in.nextInt(); int b = in.nextInt(); int k = in.nextInt(); int ans = BFS(a,b,k); System.out.println(ans); } public static int BFS(int a, int b, int k) { int n = a+b; boolean[] vis = new boolean[n+1]; Arrays.fill(vis, false); Queue<Step> q = new LinkedList<>(); Step start = new Step(a,b,0); q.offer(start); vis[a] = true; while(!q.isEmpty()) { Step s = q.poll(); if(s.a == 0) { return s.step; } for(int i = 1; i <= Math.min(s.a, k); i++) { if(s.b < k-i) { continue; } int next = s.a-i+(k-i); if(next == 0) { return s.step+1; } if(!vis[next]) { vis[next] = true; q.offer(new Step(next, n-next, s.step+1)); } } } return -1; } }
1. 很像网易游戏雷火那道推箱子题目,宽度优先。
2. 0 和 1 的个数决定当前状态,更确切说0的个数旧决定了当前状态。 如果下一步翻转i个0 ,则必然要翻转 K-m 个1,遍历0的合法的翻转个数,从而推出下一个所有可能状态。 每个状态记录到达此状态的转移步数,用mm记录, 从上一个状态到下一个状态,下一个状态步数即上一个状态步数+1 , 第一次到达0个0的状态的步数就是最短步数。
3. 注意边界条件,没有0的时候不需要翻转,输出0.
4.不要试图用标准库的unordered_map,会超时,用数组存储状态。
#include #include #include #include using std::vector; using std::cout; using std::cin; using std::string; using std::queue; int main() { int A ; int B ; int K ; cin>>B>>A>>K ; // if no zeros , no need to change, number of steps = 0 if(B==0){cout<<0;return 0;} int mm[A+B+1]; for(int i =0 ; i< A+B+1; i++)mm[i]= 0; mm[B] = 1; queue> que ; que.push({A,B}); while(!que.empty()) { vector tt = que.front(); que.pop(); int ones = tt[0]; int zeros= tt[1]; int maxE = K-ones > 0 ? K-ones:0; for(int i = zeros>K?K:zeros; i>=maxE ; i--) if(mm[zeros - i + (K-i)]==0) { if(zeros - i + (K-i) ==0){ cout <<mm[zeros]; return 0; } mm[zeros -i + (K-i)] = mm[zeros]+1; que.push({ones+i-(K-i) , zeros - i+(K-i)}); } } cout <<-1; return 0; }
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
/*
* 参考大神:https://www.nowcoder.com/questionTerminal/9c4c9d10e3db4448b906c6e6cea22b7f @OceanFan
* 有部分人使用数学推断得到结果但是比较难想,使用bfs还好理解一点
* 有起始状态和结束状态,计算其中的变化信息考虑使用bfs
* */
public class Reverse01 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
int a = scanner.nextInt();
int b = scanner.nextInt();
int k = scanner.nextInt();
System.out.println(reverse01(a, b, k));
}
}
private static int reverse01(int a, int b, int k) {
// 已经完成
if (a == 0 && b == 0) {
return 0;
}
// 不可能反转成功
if (a + b < k) {
return -1;
}
boolean[] visited = new boolean[a + b + 1];
Queue<Node> queue = new LinkedList<>();
queue.add(new Node(a, b, 0));
visited[a] = true;
while (!queue.isEmpty()) {
Node tmp = queue.poll();
if (tmp.a == 0) {
return tmp.times;
}
// 对于1~min(tmp.a,k)个0进行反转
for (int i = 1; i <= Math.min(tmp.a, k); i++) {
// 1不够反转,继续操作
if (k - i > tmp.b) {
continue;
}
int newa = tmp.a - i + (k - i);
if (newa == 0) {
return tmp.times + 1;
}
if (!visited[newa]) {
visited[newa] = true;
queue.add(new Node(newa, tmp.a + tmp.b - newa, tmp.times + 1));
}
}
}
return -1;
}
private static class Node {
int a, b, times;
public Node(int a, int b, int times) {
this.a = a;
this.b = b;
this.times = times;
}
}
}
//使用深搜,通过只有70%。。。。。。 import java.util.ArrayList; import java.util.HashMap; import java.util.Scanner; /** * Created by Administrator on 2017/8/21. * 模拟1--01翻转 */ public class Main { static int count; static int min; public static void main(String []args){ Scanner in = new Scanner(System.in); while (in.hasNext()){ int a = in.nextInt(); int b = in.nextInt(); int k = in.nextInt(); if (a==0){ System.out.println(0); continue; } count = 0; HashMap<Integer,Integer> hashMap = new HashMap<Integer, Integer>(a+b); hashMap.put(a,b); min = Integer.MAX_VALUE; solve(a,b,k,hashMap); if(min!=Integer.MAX_VALUE) System.out.println(min); else System.out.println(-1); } } private static boolean solve(int a, int b, int k, HashMap<Integer, Integer> hashMap) { if (a%k==0){ count++; min = Math.min(min,count); return true; } for (int i = 0; i <=k && i<=a; i++) { if (b<k-i){ continue; } if (!hashMap.containsKey(a-i+k-i)){ hashMap.put(a-i+k-i,b-(k-i)+i); count++; solve(a-i+k-i,b-(k-i)+i,k,hashMap); count--; hashMap.remove(a-i+k-i); } } return false; } }
/* 用个最傻的办法,遍历翻牌子的状态空间,跑的慢,不过也没超时 */ #include <iostream> #include <algorithm> #include <vector> #include <queue> using namespace std; int main(){ int a, b, k; while ((cin >> a >> b >> k)){ int n = a + b;//最大0的个数 vector<int> dp(n + 1, 0);//记录到达i个0状态的最少步数,0表示不可达 queue<int> q; //去一些最简单的情况 if (a == 0) cout << 0 << endl; else if (k >= a + b){ if (k == a) cout << 1 << endl; else cout << -1 << endl; } else{ dp[a] = 1;//初始状态 q.push(a); while (!q.empty()){ a = q.front();//当前状态a个0,n-a个1 q.pop(); for (int i = min(k, a); i >= 0; --i){//i为翻0的个数 if (k - i > n - a) break;//k-i为翻1的个数,不够翻就退出循环 //翻i个0,0的个数-i,就得翻k-i个1,0的个数+k-i,总计0个数的变化=-i+k-i=k-2*i int new_a = a + k - 2 * i;//翻完后0的个数 if (dp[new_a] == 0){ dp[new_a] = dp[a] + 1; q.push(new_a); } } } cout << dp[0] - 1 << endl; } } return 0; }
using System; using System.Collections.Generic; class Program{ public static void Main(){ string line; while((line=Console.ReadLine())!=null){ int A=int.Parse(line.Split()[0]); int B=int.Parse(line.Split()[1]); int K=int.Parse(line.Split()[2]); int[] array=new int[A+B+1]; for(int i=0;i<array.Length;i++){ array[i]=-1; } array[A]=0; Queue<int> queue = new Queue<int>(); queue.Enqueue(A); while (queue.Count > 0) { int t = queue.Dequeue(); for (int i = K % 2; i <= K; i += 2) { if (i == 0) continue; if (t + i >= 0 && t + i <= A + B && (A + B - t - i) >= (K - i) / 2 && t >= (K - i) / 2 && array[t + i] == -1) { array[t + i] = array[t] + 1; queue.Enqueue(t + i); } if (t - i >= 0 && t - i <= A + B && t - i >= (K - i) / 2 && (A + B - t) >= (K - i) / 2 && array[t - i] == -1) { array[t - i] = array[t] + 1; if (t - i == 0) { queue.Clear(); break; } queue.Enqueue(t - i); } } } Console.WriteLine(array[0]); } } }
import java.util.*; class Step { public int a; public int b; public int step; public Step(int a, int b, int step) { this.a = a; this.b = b; this.step = step; } } public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int a = in.nextInt(); int b = in.nextInt(); int k = in.nextInt(); int ans = BFS(a,b,k); System.out.print(ans); } public static int BFS(int a, int b, int k) { int n = a+b; boolean[] vis = new boolean[n+1]; Arrays.fill(vis, false); Queue<Step> q = new LinkedList<>(); Step start = new Step(a,b,0); q.offer(start); vis[a] = true; while(!q.isEmpty()) { Step s = q.poll(); if(s.a == 0) { return s.step; } for(int i = 1; i <= Math.min(s.a, k); i++) { if(s.b < k-i) { continue; } int next = s.a-i+(k-i); if(next == 0) { return s.step+1; } if(!vis[next]) { vis[next] = true; q.offer(new Step(next, n-next, s.step+1)); } } } return -1; } }
用深搜的,通过50%,一直报错“请检查是否存在数组越界等非法访问情况”,但是检查了好 几遍也没发现有数组越界的情况,是不是递归导致栈溢出了?
import java.util.*; public class Main{ static int tmin(int zeros, int ones, int k, boolean visited[]){ if(zeros == 0) return 0; if(zeros == k) return 1; if(visited[zeros]) return -1;//陷入无限循环中,无解 visited[zeros] = true; int re_max = Math.min(zeros, k);//最多反转max个0 int re_min = Math.max(0,k - ones);//最少反转min个0 //re_min可能与re _max相等 int min = -1;//无解 //i:反转i个0,剩下的用于反转1 for(int i = re_min; i <= re_max; ++i){ int nzeros = zeros - i + (k - i); int tmp = tmin(nzeros,ones+zeros-nzeros,k,visited); if(tmp == -1) continue; else if(min == -1) min = tmp; else min = Math.min(min,tmp); } if(min == -1) return -1; else return min+1; } public static void main(String[] arg){ Scanner sc = new Scanner(System.in); int zeros = sc.nextInt(), ones = sc.nextInt(), k = sc.nextInt(); if(k > ones+zeros) return ; if(k == 0 || zeros < 0 || ones < 0) return ; boolean visited[] = new boolean[zeros+ones+1]; System.out.print(tmin(zeros,ones,k,visited)); return ; } }
import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; /** * 牛牛正在挑战一款名为01翻转的游戏。游戏初始有A个0,B个1,牛牛的目标就是把所有的值都变为1,每次操作牛牛可以任意选择恰好K个数字,并将这K个数字的值进行翻转(0变为1,1变为0)。牛牛如果使用最少的操作次数完成这个游戏就可以获得奖品,牛牛想知道最少的操作次数是多少? 例如:A = 4 B = 0 K = 3 0000 -> 1110 -> 1001 -> 0100 -> 1111 需要的最少操作次数为4 * Created by Administrator on 2017/3/19. */ public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); while(sc.hasNext()){ Integer a = sc.nextInt(); Integer b = sc.nextInt(); Integer n = a+b; Integer k = sc.nextInt(); System.out.println(caozuo3(a,n,k)); } } /** BFS遍历 **/ public static int caozuo3(int a,int n,int k){ if (a==0){ return 0; } Queue<Integer> queue = new LinkedList<Integer>(); int[] visited = new int[n+1]; int[] disk = new int[n+1]; queue.offer(a); visited[a] = 1; disk[a] = 0; while (!queue.isEmpty()){ int local = queue.remove(); for (int i=1;i<=k;i++){ if (i<=local&&k-i<=n-local){ int next = local-2*i+k; if (visited[next]==0){ if (next==0)return disk[local]+1; queue.offer(next); visited[next] = 1; disk[next] = disk[local]+1; } } } } return -1; } }