教主的难题(MST)

前言

你可能需要:

  1. 最小生成树
  2. 有一定的调试能力(doge

题目

问题描述

一个数 x 可以按以下规则生成数字:

  1. 将任意两位交换,若交换的数字为 a 和 b ,生成的代价为 \text{((a and b)+(a xor b))}\times 2 。例如 134 可以生成 431 ,因为 431 可以从 134 的个位(4)与百位(1)交换后得到,代价为 \text{((1 and 4)+(1 xor 4))}\times 2=10 。
  2. 将其中一位数删除,但是删除的数要满足大等于它左边的数,且小等于它右边的数,并且定义最高位左边的数为个位,个位右边的数为最高位。若删除的数字为 a ,它左边的数为 b ,它右边的数为 c ,则生成的代价为 \text{a+(b and c)+(b xor c)} 。例如 212 ,可以删除个位的数得到 21 ,但是因为 2>1 ,所以 1 是不能被删除的。特别地,若 x 为两位数,它能删除当且仅当 x 的两位数相同,若 x 为一位数,它是不能被删除的。
  3. 在两位数字之间,也可以是数字的前面或后面加入一位数,同样地,加入的数要满足大等于它左边的数,且小等于它右边的数,并且定义最高位左边的数为个位,个位右边的数为最高位。若加入的数字为 a ,它左边的数为 b ,它右边的数为 cc ,则生成的代价为 \text{a+(b & c)+(b ^ c)} 。例如 241 ,它可以在 22 与 44 之间加入一个 33 ,生成 23412341 ,也可以在数的末尾加入一个 11 或者 22 ,当然还有其它可以生成的数,但是不能在 44 和 11 之间加入数字。

你的任务是,S 一开始为 n 个不同的给定数组成的集合,每次可以从 S 中取出一个数生成一个满足生成规则的数加入 S 中,并且取出的数仍然存在于 S 中。生成的数的位数不能大于 S 初始集合最大的数的位数。问在 S 元素最多的情况下,总代价最小是多少。

输入格式

输入的第 1 行为一个正整数 n ,为集合 S 初始元素个数。

第 2 行包含 n 个正整数 a_1,a_2,\dots,a_n,数之间空格隔开,为 S 中初始元素。

输出格式

输出包括一个正整数,为最小代价。

样例输入1

2
111 22

样例输出1

12

样例1解释

111删除1得到11,代价为2,11删除1得到1,代价为2,同样22删除和加入一个2得到2,222,代价均为4,总代价2+2+4+4=12。111无法生成1111因为111为一个3位数,而1111为一个4位数。

利用交换/添加规则无法让集合元素更多,所以最小代价为12。

数据范围及约定

对于 20\% 的数据,有 a_i<100

对于 40\% 的数据,有 a_i<1000

对于 50\% 的数据,有 n=1

对于 60\% 的数据,有 a_i<10000

对于 100\% 的数据,有 a_i<100000 ,n<5 ,保证的任何一位不包含 0 。

分析

n<5 是吧,直接暴力算出有哪些状态就完事了。

然后暴力建图从哪个状态推入哪个状态,形成一个生成图。

最后直接最小生成树求权和,搞定。

注意:收录简单,代码超标。

code

#include<bits/stdc++.h>
#define int ll
typedef long long ll;
using namespace std;
const int MAXN=6;
struct Node{
	int u,v,w;
	Node(){}
	Node(int a,int b,int c):u(a),v(b),w(c){}
	bool operator<(const Node &b){
		return w<b.w;
	}
};
vector<Node>ege;
unordered_set<int>vis;
unordered_map<int,int>gid;
int wcnt,tot,maxw,n,a[MAXN+1];
int Getwei(int vv){
	int cnt=0;
	while(vv){
		cnt++;
		vv/=10;
	}
	return cnt;
}
//vector<pair<int,int> >G[(int)1e5+1];
int nxt(int qwq){return (qwq==wcnt?1:qwq+1);}
int pre(int qwq){return (qwq==1?wcnt:qwq-1);}
int change_value(int a,int b){return ((a&b)+(a^b))*2;}
int delete_value(int a,int b,int c){return a+(b&c)+(b^c);}
int insert_value(int a,int b,int c){return a+(b&c)+(b^c);}
void BFS(int beg){
	if(vis.count(beg))return;
//	cout<<"$$$$$$$$$$$$$\n";
	vis.insert(beg);
	gid[beg]=++tot;
	queue<int>Q;
	Q.push(beg);
	while(!Q.empty()){
		int b[7]={};
		wcnt=0;
		int tmp=Q.front(),F=Q.front();Q.pop();
		while(tmp){
			b[++wcnt]=tmp%10;
			tmp/=10;
		}
		reverse(b+1,b+1+wcnt);
//		for(int i=1;i<=wcnt;i++)cout<<b[i];
//		cout<<"^^^^^^^^^^^^^"<<F<<"^^^^^^^\n";
		//////////////转移操作1////////////////
		for(int i=1;i<=wcnt;i++){
			for(int j=i+1;j<=wcnt;j++){
				swap(b[i],b[j]);
				int to=0;
				for(int k=1;k<=wcnt;k++)to=10*to+b[k];
				if(F==to)continue;
				if(!vis.count(to)){
					gid[to]=++tot;
					Q.push(to);
					vis.insert(to);
//					cout<<to<<"###\n";
				}
//				G[gid[F]].push_back(make_pair(gid[to],change_value(b[i],b[j])));
				ege.push_back((Node){gid[F],gid[to],change_value(b[i],b[j])});
				swap(b[i],b[j]);
			}
		}
		//////////////转移操作2////////////////
		if(wcnt!=1){
			for(int i=1;i<=wcnt;i++){
				if(b[i]<b[pre(i)]||b[i]>b[nxt(i)])continue;
				int to=0;
				for(int k=1;k<=wcnt;k++){
					if(k==i)continue;
					to=10*to+b[k];
				}
				if(!vis.count(to)){
					gid[to]=++tot;
					Q.push(to);
					vis.insert(to);
//					cout<<to<<"&&&&\n";
				}
//				G[gid[F]].push_back(make_pair(gid[to],delete_value(b[i],b[pre(i)],b[nxt(i)])));
				ege.push_back((Node){gid[F],gid[to],delete_value(b[i],b[pre(i)],b[nxt(i)])});
			}
		}
		//////////////转移操作3////////////////
		if(wcnt<maxw){
			for(int ins=b[wcnt];ins<=b[1];ins++){
				int to=ins;
				for(int j=1;j<=wcnt;j++)to=10*to+b[j];
				if(!vis.count(to)){
					gid[to]=++tot;
					Q.push(to);
					vis.insert(to);
//					cout<<to<<"!!!!!\n";
				}
//				G[gid[F]].push_back(make_pair(gid[to],insert_value(ins,b[wcnt],b[1])));
				ege.push_back((Node){gid[F],gid[to],insert_value(ins,b[wcnt],b[1])});
			}
			for(int i=1;i<=wcnt;i++){
				for(int ins=b[i];ins<=b[nxt(i)];ins++){
					int to=0;
					for(int k=1;k<=i;k++)to=10*to+b[k];
					to=10*to+ins;
					for(int k=i+1;k<=wcnt;k++)to=10*to+b[k];
					if(!vis.count(to)){
						gid[to]=++tot;
						Q.push(to);
						vis.insert(to);
//						cout<<to<<' '<<i<<' '<<ins<<"!!!\n";
					}
//					G[gid[F]].push_back(make_pair(gid[to],insert_value(ins,b[i],b[nxt(i)])));
					ege.push_back((Node){gid[F],gid[to],insert_value(ins,b[i],b[nxt(i)])});
				}
			}
		}
	}
	return;
}
//int dist[(int)1e5+1][MAXN+1];
//void dijkstra(int beg,int id){
//	priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >Q;
//	Q.push(make_pair(0,beg));
//	dist[beg][id]=0;
//	while(!Q.empty()){
//		auto F=Q.top();Q.pop();
//		if(F.first>dist[F.second][id])continue;
//		int from=F.second;
//		for(auto i:G[from]){
//			int to=i.first,w=i.second;
//			if(dist[to][id]>dist[from][id]+w){
//				dist[to][id]=dist[from][id]+w;
//				Q.push(make_pair(dist[to][id],to));
//			}
//		}
//	}
//	return;
//}
int fa[(int)1e5+1];
int GetFather(int x){
	if(fa[x]==0)return x;
	return fa[x]=GetFather(fa[x]);
}
signed main(){
	freopen("problem.in","r",stdin);
	freopen("problem.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",a+i);
//		cout<<Getwei(a[i])<<"$$$\n";
		maxw=max(maxw,Getwei(a[i]));
	}
	for(int i=1;i<=n;i++)BFS(a[i]);
//	cout<<gid[0]<<"###\n";
//	for(auto i:gid)printf("%lld %lld\n",i.first,i.second);
//	for(auto i:vis){
//		printf("this point is:%lld,its gid is %lld\n",i,gid[i]);
//		for(auto [to,w]:G[gid[i]])printf("%lld can go to the %lld,weight is %lld\n",i,to,w);
//	}
//	memset(dist,0x3f,sizeof dist);
	vis.clear();
	for(int i=1;i<=n;i++)vis.insert(gid[a[i]]);
//	BFS2();
	sort(ege.begin(),ege.end());
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			int fai=GetFather(gid[a[i]]),faj=GetFather(gid[a[j]]);
			if(fai==faj)continue;
			fa[fai]=faj;
		}
	}
	int ans=0;
	for(auto i:ege){
		if(GetFather(i.u)==GetFather(i.v))continue;
		ans+=i.w;
		fa[GetFather(i.u)]=GetFather(i.v);
	}
//	for(int i=1;i<=n;i++)dijkstra(gid[a[i]],i);
//	int ans=0;
//	for(int i=1;i<=tot;i++){
//		int minn=LLONG_MAX;
//		for(int j=1;j<=n;j++)minn=min(minn,dist[i][j]);
//		ans+=minn;
//		cout<<i<<' '<<minn<<"**********\n";
//	}
	printf("%lld\n",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值