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

相关文章

一文解析C#中的StringSplitOptions枚举

《一文解析C#中的StringSplitOptions枚举》StringSplitOptions是C#中的一个枚举类型,用于控制string.Split()方法分割字符串时的行为,核心作用是处理分割后... 目录C#的StringSplitOptions枚举1.StringSplitOptions枚举的常用

Kotlin 枚举类使用举例

《Kotlin枚举类使用举例》枚举类(EnumClasses)是Kotlin中用于定义固定集合值的特殊类,它表示一组命名的常量,每个枚举常量都是该类的单例实例,接下来通过本文给大家介绍Kotl... 目录一、编程枚举类核心概念二、基础语法与特性1. 基本定义2. 带参数的枚举3. 实现接口4. 内置属性三、

C#之枚举类型与随机数详解

《C#之枚举类型与随机数详解》文章讲解了枚举类型的定义与使用方法,包括在main外部声明枚举,用于表示游戏状态和周几状态,枚举值默认从0开始递增,也可手动设置初始值以生成随机数... 目录枚举类型1.定义枚举类型(main外)2.使用生成随机数总结枚举类型1.定义枚举类型(main外)enum 类型名字

C语言自定义类型之联合和枚举解读

《C语言自定义类型之联合和枚举解读》联合体共享内存,大小由最大成员决定,遵循对齐规则;枚举类型列举可能值,提升可读性和类型安全性,两者在C语言中用于优化内存和程序效率... 目录一、联合体1.1 联合体类型的声明1.2 联合体的特点1.2.1 特点11.2.2 特点21.2.3 特点31.3 联合体的大小1

C++11作用域枚举(Scoped Enums)的实现示例

《C++11作用域枚举(ScopedEnums)的实现示例》枚举类型是一种非常实用的工具,C++11标准引入了作用域枚举,也称为强类型枚举,本文主要介绍了C++11作用域枚举(ScopedEnums... 目录一、引言二、传统枚举类型的局限性2.1 命名空间污染2.2 整型提升问题2.3 类型转换问题三、C

Java 枚举的基本使用方法及实际使用场景

《Java枚举的基本使用方法及实际使用场景》枚举是Java中一种特殊的类,用于定义一组固定的常量,枚举类型提供了更好的类型安全性和可读性,适用于需要定义一组有限且固定的值的场景,本文给大家介绍Jav... 目录一、什么是枚举?二、枚举的基本使用方法定义枚举三、实际使用场景代替常量状态机四、更多用法1.实现接

C 语言中enum枚举的定义和使用小结

《C语言中enum枚举的定义和使用小结》在C语言里,enum(枚举)是一种用户自定义的数据类型,它能够让你创建一组具名的整数常量,下面我会从定义、使用、特性等方面详细介绍enum,感兴趣的朋友一起看... 目录1、引言2、基本定义3、定义枚举变量4、自定义枚举常量的值5、枚举与switch语句结合使用6、枚

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

C#实现获得某个枚举的所有名称

《C#实现获得某个枚举的所有名称》这篇文章主要为大家详细介绍了C#如何实现获得某个枚举的所有名称,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下... C#中获得某个枚举的所有名称using System;using System.Collections.Generic;usi

Java 枚举的常用技巧汇总

《Java枚举的常用技巧汇总》在Java中,枚举类型是一种特殊的数据类型,允许定义一组固定的常量,默认情况下,toString方法返回枚举常量的名称,本文提供了一个完整的代码示例,展示了如何在Jav... 目录一、枚举的基本概念1. 什么是枚举?2. 基本枚举示例3. 枚举的优势二、枚举的高级用法1. 枚举