首页 > 其他分享 >UVA1352 题解

UVA1352 题解

时间:2023-09-10 13:55:40浏览次数:46  
标签:24 UVA1352 题解 ++ int 立方体 rot left

思路分析

构造排列表

立方体只有 \(4\) 个,暴力法是可行的。但是如果我们要暴力,首先得清楚一个立方体到底有几种不同的旋转方式。

接下来,我们用“姿态”一词代替“旋转方法”。假设 \(6\) 个面的编号为 \(1\sim6\),从中选择一个面作为“顶面”,“顶面”的对面为“底面”。然后我们在剩下来的 \(4\) 个面中选一个作为“正面”,则其他面都可以唯一确定,因此有 \(4\times6=24\) 种姿态。

在代码中,每一种姿态对应一个全排列 \(P\)。其中,\(P_i\) 代表编号 \(i\) 所在的位置。

接下来,我们可以用代码来实现排列表,详见代码实现一栏。

暴力出奇迹

我们应该如何暴力呢?一种方法是枚举最后那个“相同的立方体”,然后对于每个立方体,看看哪种姿态需要重新涂色的面最少。但由于 \(4\) 个立方体可能会有 \(24\) 种不同的颜色,需要枚举 \(24^6\) 种情况,有超时的风险。

另一种方法是先枚举每个立方体的姿态(第一个作为“参考系”,不用旋转),然后对于 \(6\) 个面,分别需哪一个出现次数最多的颜色作为“标准”,和它不同的颜色一律重涂。由于每个立方体的姿态有 \(24\) 种,而需要枚举的有 \(3\) 个立方体,所以我们只需要枚举 \(24^3\) 种情况,稳打稳能通过这道题。

代码实现

// 构造排列表代码
#include <cstdio>
#include <cstring>
using namespace std;
int left[] = {4, 0, 2, 3, 5, 1};
int up[] = {2, 1, 5, 0, 4, 3};
// 按照排列T旋转姿态p
void rot(int *T, int *p) {
	int q[6];
	memcpy(q, p, sizeof(q));
	for (int i = 0; i < 6; i++) p[i] = T[q[i]];
}
void solve() {
	int p0[6] = {0, 1, 2, 3, 4, 5};
	printf("int dice[24][6] = {\n");
	for (int i = 0; i < 6; i++) {
		int p[6];
		memcpy(p, p0, sizeof(p0));
		switch (i) {
		case 0:
			rot(up, p);
			break;
		case 1:
			rot(left, p);
			rot(up, p);
			break;
		case 3:
			rot(up, p);
			rot(up, p);
			break;
		case 4:
			rot(left, p);
			rot(left, p);
			rot(up, p);
			break;
		case 5:
			rot(left, p);
			rot(left, p);
			rot(left, p);
			rot(up, p);
			break;
		}
		for (int j = 0; j < 4; j++) {
			printf("{%d, %d, %d, %d, %d, %d},\n", p[0], p[1], p[2], p[3], p[4], p[5]);
			rot(left, p);
		}
	}
	printf("};\n");
}
int main() {
	solve();
	return 0;
}

// 最终代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 4;
const int dice24[24][6] = { // 通过上一份代码生成的排列表
	{2, 1, 5, 0, 4, 3},
	{2, 0, 1, 4, 5, 3},
	{2, 4, 0, 5, 1, 3},
	{2, 5, 4, 1, 0, 3},
	{4, 2, 5, 0, 3, 1},
	{5, 2, 1, 4, 3, 0},
	{1, 2, 0, 5, 3, 4},
	{0, 2, 4, 1, 3, 5},
	{0, 1, 2, 3, 4, 5},
	{4, 0, 2, 3, 5, 1},
	{5, 4, 2, 3, 1, 0},
	{1, 5, 2, 3, 0, 4},
	{5, 1, 3, 2, 4, 0},
	{1, 0, 3, 2, 5, 4},
	{0, 4, 3, 2, 1, 5},
	{4, 5, 3, 2, 0, 1},
	{3, 4, 5, 0, 1, 2},
	{3, 5, 1, 4, 0, 2},
	{3, 1, 0, 5, 4, 2},
	{3, 0, 4, 1, 5, 2},
	{1, 3, 5, 0, 2, 4},
	{0, 3, 1, 4, 2, 5},
	{4, 3, 0, 5, 2, 1},
	{5, 3, 4, 1, 2, 0},
};
int n, dice[maxn][6], ans;
int r[maxn], color[maxn][6];
vector<string> names;
int ID(const char *name) {
	string s(name);
	size_t len = names.size();
	for (size_t i = 0; i < len; i++)
		if (names[i] == s) return i;
	names.push_back(s);
	return len;
}
void check() {
	for (int i = 0; i < n; i++)
		for (int j = 0; j < 6; j++)
			color[i][dice24[r[i]][j]] = dice[i][j];
	int tot = 0; // 需要重新涂色面数
	for (int j = 0; j < 6; j++) { // 考虑每个面
		int cnt[maxn * 6], maxface = 0; // cnt表示每种颜色出现的次数
		memset(cnt, 0, sizeof(cnt));
		for (int i = 0; i < n; i++)
			maxface = max(maxface, ++cnt[color[i][j]]);
		tot += n - maxface;
	}
	ans = min(ans, tot);
}
void dfs(int d) {
	if (d == n) {
		check();
		return ;
	}
	for (int i = 0; i < 24; i++) {
		r[d] = i;
		dfs(d + 1);
	}
}
int main() {
	while (scanf("%d", &n) == 1 && n) {
		names.clear();
		for (int i = 0; i < n; i++)
			for (int j = 0; j < 6; j++) {
				char name[30];
				scanf("%s", name);
				dice[i][j] = ID(name);
			}
		ans = n * 6; // 最终值的上界
		r[0] = 0; // 第一个立方体不用旋转
		dfs(1);
		printf("%d\n", ans);
	}
	return 0;
}

标签:24,UVA1352,题解,++,int,立方体,rot,left
From: https://www.cnblogs.com/IShallReturn/p/UVA1352-ti-jie.html

相关文章

  • UVA11464 题解
    思路分析暴力枚举?我们可以枚举每个数字变或不变,最后判断整个矩阵是否满足条件。但是,这样做最多需要枚举\(2^{255}≈5\times10^{67}\)中情况,是一定会超时的。大眼观察注意到\(n\le15\),第一行只有不超过\(2^{15}=32768\)种可能,所以第一行的情况是可以枚举的。接下来,根据第......
  • 题解 SP4586 Texas Trip
    首先题目翻译是有问题的,求的不是矩形而是最小的正方形。Solution先考虑一下若正方形的边都只能平行于坐标轴怎么做:找到\(x,y\)方向的坐标最值,那么答案就是\(\max^2\{X_{\max}-X_{\min},Y_{\max}-Y_{\min}\}\)。接下来,若正方形可以是斜着的,我们只需把坐标轴旋转,然后同样找出......
  • 题解 P8389【[COI2021] Izvanzemaljci】
    (本题解的所有图片使用GeometryWidget进行绘制)(一)\(K=1\)情况\(K=1\)是平凡的。(二)\(K=2\)情况显然,对于平面内的两个不交正方形,存在至少一条平行于坐标轴的直线将它们划分到两侧。以直线平行于\(y\)轴为例。考虑按\(x\)轴正方向扫描线。先将点按照\(x\)坐标排序,维......
  • cnpm : 无法加载文件 \npm\cnpm.ps1,因为在此系统上禁止运行脚本。 问题解决
    很久没有弄前端VUE了。最近用到的vscode进行前端摸索。在用NPM的时候,发现有点慢,于是切换到了cnpm。在使用cnpm进行运行的时候报错了。“cnpm:无法加载文件C:\Users\Administrator\AppData\Roaming\npm\cnpm.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅https:/go.mi......
  • 【题解】三连击
    [NOIP1998普及组]三连击思路想一想桶得到三个数之后把每一位依次存入桶然后遍历这个桶,看哪一位为\(0\)代码//语言:C++#include<iostream>#include<cstring>//memsetusingnamespacestd;intmain(){ for(inti=123;i<=987/3;i++) { inta=i,b=2*i,c=3*i; i......
  • P3533 [POI2012] RAN-Rendezvous 题解
    P3533[POI2012]RAN-Rendezvous题目大意:给定外向树森林,每次给定两个起始点,求两个点沿边移动最少步数相遇。\(n\)个点,\(n\)条边,并且每个点有唯一的出边,显然构成了多棵基环树,对于每个基环树分别处理:找出环上的点,因为要求支持求出任意两点距离,前缀和一下即可。对于询问,如果在两......
  • vncviewer黑屏问题解决
    如果不知道kill多少值,可通过ps-ef|grepvnc进行查看1.先kill掉现在的vnc端口进程(假设端口是1):比如:vncserver-kill:12.打开启动文件xstartup:vim~/.vnc/xstartup3.修改其中的内容如下:#!/bin/shexportXKL_XMODMAP_DISABLE=1unsetSESSION_MANAGERunsetDBUS_SESSION_BUS_AD......
  • CF1570D 题解
    思路分析前言题解区好似没有用哈希的啊。发现大家都在用map来存是否出现过数字,但是需要注意的是,map的单次查询时间复杂度是\(\mathcalO(\logn)\)的,对于大规模的数据就很可能会TLE。所以,我们可以使用哈希的方法来判断数字是否出现过。浅谈哈希哈希,是通过哈希函数将数......
  • 【题解】Educational Codeforces Round 143(CF1795)
    A.TwoTowers题目描述:有\(a,b\)两座由红蓝色方块垒成的塔,其中\(a\)的高度为\(n\);\(b\)的高度为\(m\),用R代表红色;用B代表蓝色。你可以多次把其中一座顶端的方块移到另一座的顶端(可以不移动)。问有没有一种方法可以使两座塔中均没有连续的同颜色方块。题目分析:可以......
  • UVA202 题解
    思路分析前言又是一道小模拟题,不过细节巨多,可以用来锻炼自己的代码能力。解法本题实际上就是模拟长除法的计算过程,其中每一步除法时都有被除数及其余数,当被除数出现重复时就表示出现循环节了。所以需要记录每一次的被除数及其在循环小数中的位置,需要判断当除数不够除,每一次补......