Eight Digital Games Gym - 102623E(建图,枚举排列)

2024-04-16 00:48

本文主要是介绍Eight Digital Games Gym - 102623E(建图,枚举排列),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Setsuna has been obsessed with an electronic video game called “Eight Digital”. It is a puzzle game which is too difficult for Setsuna, so she has been stuck in the first level for a long time.

In the game, you have a string of length 𝑛 containing only digits from 1 to 8. Let’s call this string 𝑆, 𝑆𝑖 denotes the 𝑖-th character of 𝑆.

At the end of the game, the game system will calculate the penalties of inversions, and will deduct your coins. Inversion is a pair (𝑖,𝑗) which satisfies both 𝑖<𝑗 and 𝑆𝑖>𝑆𝑗. The game system will deduct you 𝑃𝑆𝑖,𝑆𝑗 coins for each inversion (𝑖,𝑗).

For example, string 85511 will cost you 2×𝑃8,5+2×𝑃8,1+4×𝑃5,1 coins, and string 1234 will cost you 0 coins.

Before the end of the game, you can do arbitrary times of the operation. Each operation is to select two different numbers 𝑥 and 𝑦(1≤𝑥,𝑦≤8), then all 𝑥 in the string will become 𝑦, and all 𝑦 will become 𝑥 and the game system will deduct you 𝐶𝑥,𝑦 coins.

For example, you can spend 𝐶1,3 to convert string 131233 into string 313211.

To help poor girl Setsuna, you are required to find the minimum cost of coins to finish the game.

Input
The first line contains one integer 𝑛(1≤𝑛≤105).

The second line contains a string 𝑆. It is guaranteed that the length of 𝑆 is 𝑛 and 𝑆 only contains digits from 1 to 8.

The next 8 lines contains 8 integers each, where the 𝑗-th integer of the 𝑖-th line is 𝑃𝑖,𝑗(0≤𝑃𝑖,𝑗≤107). It is guaranteed that 𝑃𝑖,𝑗=0 holds for 𝑖≤𝑗.

The next 8 lines contains 8 integers each, where the 𝑗-th integer of the 𝑖-th line is 𝐶𝑖,𝑗(0≤𝐶𝑖,𝑗≤1012). It is guaranteed that 𝐶𝑖,𝑖=0 and 𝐶𝑖,𝑗=𝐶𝑗,𝑖.

Output
Output one integer indicating the minimum cost.

Examples
inputCopy
5
54321
0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0
1 1 1 0 0 0 0 0
1 1 1 1 0 0 0 0
1 1 1 1 1 0 0 0
1 1 1 1 1 1 0 0
1 1 1 1 1 1 1 0
0 1 1 1 1 1 1 1
1 0 1 1 1 1 1 1
1 1 0 1 1 1 1 1
1 1 1 0 1 1 1 1
1 1 1 1 0 1 1 1
1 1 1 1 1 0 1 1
1 1 1 1 1 1 0 1
1 1 1 1 1 1 1 0
outputCopy
2
inputCopy
6
222112
0 0 0 0 0 0 0 0
5 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0
1 1 1 0 0 0 0 0
1 1 1 1 0 0 0 0
1 1 1 1 1 0 0 0
1 1 1 1 1 1 0 0
1 1 1 1 1 1 1 0
0 1 2 2 2 2 2 2
1 0 2 2 2 2 2 2
2 2 0 2 2 2 2 2
2 2 2 0 2 2 2 2
2 2 2 2 0 2 2 2
2 2 2 2 2 0 2 2
2 2 2 2 2 2 0 2
2 2 2 2 2 2 2 0
outputCopy
4
Note
In sample 1, you can spend 1 coin to convert 54321 into 14325. Then, spend another 1 coin to convert it into 12345.

In sample 2, you can spend 2 coins to convert 222112 into 222882, then end the game. The inversions (4,6) and (5,6) will cost you 2×𝑃8,2=2 coins. So the total cost is 4 coins.

题意:
只由1~8组成的序列,不同的逆序对会产生一定的花费。你可以用一定的花费将两种数字全部交换。求产生的最小花费。

思路:
参考博客:https://blog.csdn.net/ECNU_SF/article/details/106584769

我觉得关键点就在于怎么操作的花费和操作完毕后逆序对的花费。

首先是操作的花费:
本题中的操作可以抽象为两个排列的映射。
如1 2 3 4 5 6 7 8映射到4 3 2 5 1 7 8 6,
那么(2,1)就变成了(4,3),(4,5)就变成了(5,1),以此类推。

我们可以将排列当做点,那么排列间的变换就是边,跑一遍最短路就知道排列(也就是操作)的花费了。

然后是逆序对的花费:
我们维护 d p [ i ] [ j ] dp[i][j] dp[i][j]代表数对 ( i , j ) (i,j) (i,j)的个数,
那么操作完成之后,数对 ( i , j ) (i,j) (i,j)会映射成 ( n u m [ i ] , n u m [ j ] ) (num[i],num[j]) (num[i],num[j]),对应的花费就是 d p [ i ] [ j ] ∗ P [ n u m [ i ] ] [ n u m [ j ] ] dp[i][j]*P[num[i]][num[j]] dp[i][j]P[num[i]][num[j]]

几个tips:

  1. 排列的编号怎么求:最直接的想法就是把排列当成8位数字,然后枚举一遍全排列再映射一下。看了题解发现,是有组合数学的方法(就是下面的perm2num函数)。
  2. 关于排列之间怎么建边:交换两个位置, C [ i ] [ j ] C[i][j] C[i][j] C [ a [ i ] ] [ a [ j ] ] C[a[i]][a[j]] C[a[i]][a[j]]的边权都是可以的。前者是逆序变化,后者是顺序变化。
#include <ctime>
#include <iostream>
#include <assert.h>
#include <vector>
#include <queue>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <set>
#include <map>using namespace std;
typedef long long ll;const int maxn = 1e6 + 7;
const int mod = 998244353;ll d[maxn],P[10][10],C[10][10],dp[10][10];
int fac[10],cnt[maxn],vis[maxn];char s[maxn];struct Node {int a[10];ll cost;bool operator < (const Node &res) const {return cost > res.cost;}
};int perm2num(int* a) {int res = 0;for(int i = 1; i < 8; i++) {int cnt = 0;for(int j = i + 1; j <= 8; j++)if(a[i] > a[j])cnt++;res += cnt * fac[8 - i];}return res;
}void dijkstra() {priority_queue<Node>q;Node fir;for(int i = 1;i <= 8;i++) {fir.a[i] = i;}fir.cost = 0;q.push(fir);memset(d,0x3f,sizeof(d));d[perm2num(fir.a)] = 0;while(!q.empty()) {Node now = q.top();q.pop();int x = perm2num(now.a);if(vis[x]) continue;vis[x] = 1;Node nex = now;for(int i = 1;i <= 8;i++) {for(int j = i + 1;j <= 8;j++) {if(i == j) continue;swap(nex.a[i],nex.a[j]);int y = perm2num(nex.a);if(d[y] > d[x] + C[nex.a[i]][nex.a[j]]) {d[y] = d[x] + C[nex.a[i]][nex.a[j]];nex.cost = d[y];q.push(nex);}swap(nex.a[i],nex.a[j]);}}}
}int main() {fac[0] = 1;for(int i = 1;i <= 8;i++) {fac[i] = fac[i - 1] * i;}int n;scanf("%d",&n);scanf("%s",s + 1);for(int i = 1;i <= 8;i++) {for(int j = 1;j <= 8;j++) {scanf("%lld",&P[i][j]);}}for(int i = 1;i <= 8;i++) {for(int j = 1;j <= 8;j++) {scanf("%lld",&C[i][j]);}}for(int i = 1;i <= n;i++) {int now = s[i] - '0';for(int j = 1;j <= 8;j++) {dp[j][now] += cnt[j];}cnt[now]++;}dijkstra();int num[10] = {0,1,2,3,4,5,6,7,8};ll ans = 1e18;do {int idx = perm2num(num);ll res = d[idx];for(int i = 1;i <= 8;i++) {for(int j = 1;j <= 8;j++) {res += dp[i][j] * P[num[i]][num[j]];}}ans = min(ans,res);}while(next_permutation(num + 1,num + 1 + 8));printf("%lld\n",ans);return 0;
}

这篇关于Eight Digital Games Gym - 102623E(建图,枚举排列)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/907375

相关文章

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

hdu 2489 (dfs枚举 + prim)

题意: 对于一棵顶点和边都有权值的树,使用下面的等式来计算Ratio 给定一个n 个顶点的完全图及它所有顶点和边的权值,找到一个该图含有m 个顶点的子图,并且让这个子图的Ratio 值在所有m 个顶点的树中最小。 解析: 因为数据量不大,先用dfs枚举搭配出m个子节点,算出点和,然后套个prim算出边和,每次比较大小即可。 dfs没有写好,A的老泪纵横。 错在把index在d

poj 3181 网络流,建图。

题意: 农夫约翰为他的牛准备了F种食物和D种饮料。 每头牛都有各自喜欢的食物和饮料,而每种食物和饮料都只能分配给一头牛。 问最多能有多少头牛可以同时得到喜欢的食物和饮料。 解析: 由于要同时得到喜欢的食物和饮料,所以网络流建图的时候要把牛拆点了。 如下建图: s -> 食物 -> 牛1 -> 牛2 -> 饮料 -> t 所以分配一下点: s  =  0, 牛1= 1~

hdu 6198 dfs枚举找规律+矩阵乘法

number number number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Problem Description We define a sequence  F : ⋅   F0=0,F1=1 ; ⋅   Fn=Fn

【Rust练习】12.枚举

练习题来自:https://practice-zh.course.rs/compound-types/enum.html 1 // 修复错误enum Number {Zero,One,Two,}enum Number1 {Zero = 0,One,Two,}// C语言风格的枚举定义enum Number2 {Zero = 0.0,One = 1.0,Two = 2.0,}fn m

枚举相关知识点

1.是用户定义的数据类型,为一组相关的常量赋予有意义的名字。 2.enum常量本身带有类型信息,即Weekday.SUN类型是Weekday,编译器会自动检查出类型错误,在编译期间可检查错误。 3.enum定义的枚举类有什么特点。         a.定义的enum类型总是继承自java.lang.Enum,且不能被继承,因为enum被编译器编译为final修饰的类。         b.只能定义

PHP字符串全排列

方法一: $str = 'abc';$a =str_split($str);perm($a, 0, count($a)-1);function perm(&$ar, $k, $m) {if($k == $m){ echo join('',$ar), PHP_EOL;}else {for($i=$k; $i<=$m; $i++) {swap($ar[$k], $ar[$i]);perm($ar

回溯——9.全排列

力扣题目链接 给定一个 没有重复 数字的序列,返回其所有可能的全排列。 示例: 输入: [1,2,3]输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 解题思路 问题建模:题目要求给出一个数组的所有排列组合,属于典型的全排列问题,这可以用回溯法来解决。回溯法通过递归的方式,依次将数组中的每个元素放入排列中,直到生成

【C语言】结构体、枚举、联合体

【C语言】结构体、枚举、联合体 文章目录 @[TOC](文章目录) 前言一、结构体声明1.一般格式2.typedef 重命名结构体类型定义变量 二、结构体数组三、结构体与指针及函数传参四、结构体传参五.结构体在内存的存储六、参考文献总结 前言 使用工具: 1.编译器:VScode 2.C Primer Plus 第六版-1 提示:以下是本篇文章正文内容,下面案例可供参考

代码随想录刷题day25丨491.递增子序列 ,46.全排列 ,47.全排列 II

代码随想录刷题day25丨491.递增子序列 ,46.全排列 ,47.全排列 II 1.题目 1.1递增子序列 题目链接:491. 非递减子序列 - 力扣(LeetCode) 视频讲解:回溯算法精讲,树层去重与树枝去重 | LeetCode:491.递增子序列_哔哩哔哩_bilibili 文档讲解:https://programmercarl.com/0491.%E9%80%92%E