025.抽象打表
小红写谱

先看抽象打表做法
-
首先注意到重复的按键可以忽略,只要把它们连着排贡献就都是0
这样就变成了一个长度最大为 8 的数组 -
对于 1 ~ 8 的所有组合 :2^8
去掉空集就是 2^8 - 1 -
对一个长度为 n 的组合它的所有排列数 : n !
-
总代价为
\[\sum_{i=1}^{8}i *i!
\]
-
数量级为 1e6
-
建立映射,常数处理询问
code
实现:
递归枚举组合,嵌套递归枚举排列
对组合建立映射 1 2 3 5 8 的key值为12358
#include<bits/stdc++.h>
using namespace std;
vector<int>ans(12345679);
int sum;
vector<int>p;
vector<int>a;
bitset<10>vis;
void dfs(){
if(p.size()==a.size()){
int t=0;
for(int i=1;i<(int)p.size();++i){
t+=min(abs(p[i]-p[i-1]),8-abs(p[i]-p[i-1]));
}
sum=min(sum,t);
return;
}
for(int i=0;i<(int)a.size();++i){
if(vis[i])continue;
vis[i]=1;
p.push_back(a[i]);
dfs();
p.pop_back();
vis[i]=0;
}
}
void dfs1(int x,int key){
if(x>8){
if(a.empty())return;//去掉空集
sum=INT_MAX;
p.clear();
vis.reset();
dfs();
ans[key]=sum;
return;
}
dfs1(x+1,key);
a.push_back(x);
dfs1(x+1,key*10+x);
a.pop_back();
}
int main(){
dfs1(1,0);
int T;
cin>>T;
while(T--){
int n;
cin>>n;
vector<int>cnt(9);
while(n--){
int t;cin>>t;
cnt[t]++;
}
int key=0;
for(int i=1;i<=8;++i){
if(cnt[i]){
key=key*10+i;
}
}
cout<<ans[key]<<'\n';
}
}
正解
-
首先还是重复的按键没有贡献
-
顺时针/逆时针转一圈代价最小
-
所以 :首尾连成一个环、枚举起点转一圈即可
其实这等价为转一整圈的代价(8)- 距离最远的两个键的距离
这不就是题目的情境吗 !!
舞萌!
#include<bits/stdc++.h>
using namespace std;
int main(){
int T;
cin>>T;
while(T--){
int n;
cin>>n;
vector<int>cnt(9);
for(int i=0;i<n;++i){
int a;
cin>>a;
cnt[a]++;
}
vector<int>a;
for(int i=1;i<=8;++i){
if(cnt[i]){
a.push_back(i);
}
}
int m=0,p=a.back()-8;
for(int x:a){
m=max(m,x-p);
p=x;
}
cout<<8-m<<'\n';
}
}
I am the bone of my sword

浙公网安备 33010602011771号