这4个题目的联系实在太紧密了,以至于,代码好多可以重用
题目:
Description
Input
Output
Sample Input
6 3 1 3 1 2 1 1 3 1 3 1 1 1 2 6 3 6 5 4 1 1 2 3 2 6 3 6 5 4 2 3 2 1 1 3 1 3 1 2 1 1 20 2 20 17 2 19 18 16 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
Sample Output
true false false false true true
因为我是先做了汉诺塔VIII然后才做的汉诺塔VII(本题),所以是直接用的汉诺塔VIII的结论做的。
其实就是求是否存在这样的一个m,使得m次移动之后的状态就是题目所给的状态。
如果我输出的是true,其实这个时候的m的值表示的刚好就是,m次移动之后的状态就刚好满足。
那么m到底怎么求呢?显然和搜索无关。
其实就是n个方程,汉诺塔VIII里面是m已知,那么n个方程依次可以求出每个盘子在哪。
反过来,就是关于m的一元n次方程组,问是否有解。
如果把m看成x1+2 * x2+4 * x3+8 * x4。。。其中xi是0或者1,即m的每一位,那么该方程就是n元n次方程组。
而且本题不需要求解方程组的方法,只需要依次求出各个xi即可(或者判定为无解)
相当于系数矩阵为下三角矩阵的n元n次方程组,只要每次都把前面已经求出来的解代入,即可求出1解,直到全部求出。
代码:
#includeusing namespace std; int list[65]; int main() { int t, n, l, a, f; long long m; cin >> t; while (t--) { cin >> n; for (int i = 0; i < 3; i++) { cin >> l; while (l--) { cin >> a; list[a] = i; } } m = 0; bool flag = false; for (int i = n; i>0; i--) { m *= 2; f = ((i % 2) * 2 - 1)*((n % 2) * 2 - 1)*list[i]; if ((m - f) % 3 == 0)continue; else if (((m - f) % 3 + 2) % 3 == 0)m += 1; else { flag = true; break; } } if (flag)cout << "false" << endl; else cout << "true" << endl; } return 0; }
题目:
Description
Input
Output
Sample Input
3 3 2 4 5 39 183251937942
Sample Output
1 3 1 2 1 1 2 4 1 1 3 1 2 13 39 36 33 30 27 24 21 18 15 12 9 4 1 12 38 35 32 29 26 23 20 17 14 11 8 5 14 37 34 31 28 25 22 19 16 13 10 7 6 3 2
代码:
//我惊奇的发现其实我的输出结果,连n=4,m=5的时候都不对
//当n为偶数的时候,后面2行我输出的是反的
#include
#include
using namespace std;
stack<int>s[3];
int main()
{
int t;
cin >> t;
long long n, g, p, f;
long long m;
long long a = 1;
while (t--)
{
cin >> n >> m;
for (int i = 0; i < 3; i++)while (!s[i].empty())s[i].pop();
for (int i = 1; i <= n; i++)
{
g = (m >> (i + 1)) % 3;
f = (i % 2) * 2 - 1;
p = (f*(g - ((m&(a << i))>0) - ((m&(a << (i - 1)))>0) + 3) % 3 + 3) % 3;
s[p].push(i);
}
for (int i = 0; i < 3; i++)
{
cout << s[i].size();
while (!s[i].empty())
{
cout << " " << s[i].top();
s[i].pop();
}
cout << endl;
}
}
return 0;
}
吓得我赶紧重写重提交,又AC了一次。顺便把m改成了每次都除2。
代码:
#include
#include
using namespace std;
stack<int>s[3];
int main()
{
int t;
cin >> t;
long long n, p, f;
long long m;
while (t--)
{
cin >> n >> m;
for (int i = 0; i < 3; i++)while (!s[i].empty())s[i].pop();
for (int i = 1; i <= n; i++)
{
f = ((i % 2) * 2 - 1)*((n % 2) * 2 - 1); //移动的方向
p = (f*(m + m % 2) % 3 + 3) % 3; //移动的距离
m /= 2;
s[p].push(i);
}
for (int i = 0; i < 3; i++)
{
cout << s[i].size();
while (!s[i].empty())
{
cout << " " << s[i].top();
s[i].pop();
}
cout << endl;
}
}
return 0;
}
原理就不解释了,可以把m化成二进制观察规律。(关于m的运算可以看看上面汉诺塔VII的讲解,还有下面的题目)
题目:
Description
Input
Output
Sample Input
63 1 63 2 0 0
Sample Output
1 2
这个题目的规律描述起来比较简单。假设m的最后一个1在从右往左第i位,那么第i次操作就是移动第i个盘子。
有个约瑟夫问题和这个差不多点击打开我的博客
关于约瑟夫问题和汉诺塔问题的紧密联系,别人都做了很多研究,我就不扯了,反正都是二进制就对了。
其实上面的汉诺塔VIII是用到了这个结论的,不过我没有明说出来。
代码:
#includeusing namespace std; int main() { int n; long long m; while (cin >> n >> m) { if (n == 0)break; int i = 1; while (m % 2 == 0) { i++; m /= 2; } cout << i << endl; } return 0; }
题目:
Description
Input
Output
Sample Input
4 3 2 4 5 39 183251937942 63 3074457345618258570
Sample Output
2 1 2 1 3 1 2 2 3 2 2 3
这个题目,真的是。。。把上面2个题目的代码杂交,得到的就是正确的代码
代码:
#includeusing namespace std; int main() { int t, n; long long m; cin >> t; while (t--) { cin >> n >> m; if (n == 0)break; int i = 1; while (m % 2 == 0) { i++; m /= 2; } cout << i << " "; int f = ((i % 2) * 2 - 1)*((n % 2) * 2 - 1); //移动的方向 cout << (f*((m - 1) + (m - 1) % 2) % 3 + 3) % 3 + 1 << " " << (f*(m + m % 2) % 3 + 3) % 3 + 1 << endl; } return 0; }