1. 01反转
题目
牛牛正在挑战一款名为01翻转的游戏。游戏初始有A个0,B个1,牛牛的目标就是把所有的值都变为1,每次操作牛牛可以任意选择恰好K个数字,并将这K个数字的值进行翻转(0变为1,1变为0)。牛牛如果使用最少的操作次数完成这个游戏就可以获得奖品,牛牛想知道最少的操作次数是多少?
例如:A = 4 B = 0 K = 30000 -> 1110 -> 1001 -> 0100 -> 1111
需要的最少操作次数为4
思路
使用广度优先搜索,倒着向后遍历,从最终状态到起始状态。
因为每条路径上边的全都都可以看作为1,所以如果之前遍历了,那么之前的一定比后面的路径(步数)少,所以之前遍历了,后面再遇到就跳过。
#include <iostream>
#include <deque>
#include <vector>
using namespace std;
struct Node
{
Node(int a, int step, int f)
: a(a), step(step), f(f)
{
}
int a; //当前1的个数
int step; //距离权为1的步数
int f; //上次1的个数
};
//a:1的个数,b:0的个数,k每次反转的次数。
int turn0to1Counts(int a, int b, int k)
{
int total = a + b;
vector<Node> memo(total + 1, Node(-1, -1, -1));
deque<Node> de;
//将最后一步放入队列
Node end{total, 0, -1};
de.push_back(end);
//每次可能盛夏的
int remain1;
memo[total] = Node(total, 0, -1);
cout << "de add: " << de.back().a << " "
<< de.back().step << " " << de.size() << endl;
while (!de.empty())
{
Node n = de.front();
cout << "now : " << n.a << " " << n.step << " " << n.f << endl;
//如果目前正在处理的节点,等于初始状态,表明,已经遍历到了最终结果。
if (n.a == a)
{
return n.step;
}
de.pop_front();
cout << "\n_______" << n.a << " :from_______" << endl;
//判断可能的反转情况:
//反转1变为0的个数:从0到目前的1正面的数目,依次遍历,但不能超过k
//因为不能一次反转k+1个1,所以,大于的要略去
for (int i = 0; i <= n.a && i<=k; i++)
{
//原有1的个数+每次能反转的K个数-反转的1个数 应该小于总数
//这里相当于,两个部分叠加,减去重叠部分的长度,不能超过总长
if (n.a+k-i<=total)
{
remain1 = n.a -i + (k-i);
//记录已经出现,那么就不要在遍历了。
if (memo[remain1].a == -1)
{
de.push_back(Node(remain1, n.step + 1, n.a));
memo[remain1] = Node(remain1, n.step + 1, n.a);
cout << "de add: " << de.back().a << " "
<< de.back().step << " " << de.size() << endl;
}
}
}
cout << "_______" << n.a << "_______\n" << endl;
}
//上面没有找到可能的反转
return -1;
}
int main()
{
int a, b, k;
cin >> a >> b >> k;
cout << "count :" << turn0to1Counts(a, b, k) << endl;
}
上面主要在于判断,下一次反转1可能的个数。
理清这个就好了。
(可怜的我,理了一晚上,才搞出来)
网友评论