2021-7-15 个人赛 补题

Do You Know Your ABCs? 2

题意:
给你N个数( 4<=N<=7 ),分别是A、B、C、A+B、A+C、B+C、A+B+C中的N中;
已知1<=A<=B<=C,求根据给出的数,能确定多少种三元组(A.,B,C);

思路:
根据鸽巢原理,从给定的至少四个数中,一定能得出A+B+C,不是已经得到了就是可以从已知的某两个数中加出来,这样就得到了A+B+C;
同样根据鸽巢原理,我们用得到的A+B+C减去原来的 X 那么至少能得到A、B、C中的两个,这样我们从原来的N个数和A+B+C相减的数中的2N个数中暴力枚举A、B、C中的两个,第三个用A+B+C相减得到,再去判断原来的N个数是不是能够得到即可,用set<vector >来去重三元组。

int n,m,k,t;
int s[10];
map<int,int>vis;
vector<int>v;
set<vector<int> >ss;
vector<int>p;
vector<int>pp;
void check(int a,int b,int c){
	v.clear();
	v.push_back(a);v.push_back(b);v.push_back(c);
	sort(v.begin(),v.end());
	if(v[0]>=1){
		vis.clear();
		vis[a]=1;vis[b]=1;vis[c]=1;
		vis[a+b]=1;vis[a+c]=1;vis[b+c]=1;
		vis[a+b+c]=1;
		int f=1;
		for(int i=1;i<=n;i++){
			if(!vis[s[i]])f=0;
		}
		if(f)ss.insert(v);
	}
	
}

void solve(int x){
	pp.clear();
	for(int i=1;i<=n;i++){
		pp.push_back(s[i]);
		pp.push_back(x-s[i]);
	}
	for(int i=0;i<pp.size();i++){
		for(int j=i+1;j<pp.size();j++){
			check(pp[i],pp[j],x-pp[i]-pp[j]);
		}
	}
}
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		vis.clear();
		p.clear();
		ss.clear();
		for(int i=1;i<=n;i++){
			scanf("%d",&s[i]);
			p.push_back(s[i]);
		}
		for(int i=1;i<=n;i++){
			for(int j=i+1;j<=n;j++)p.push_back(s[i]+s[j]);
		}
		for(int i=0;i<p.size();i++){
			solve(p[i]);
		}
		printf("%d\n",ss.size());
	}
	return 0;
}

第一次知道还可以 set< vector > 的,之前都想得是 用node来存储,但是结构体放到set里面要自定义哈希规则,可以这样也太方便了,直接定义vector,某个位置存储想要的值就可以了。·

Portals

题意:
给你n个顶点的图,每个顶点连接四条边,总共 2*n 条边;
这张图有两种移动方式:
第一、沿着边从一个顶点到另一个顶点;
第二、从这个顶点的第一条路到第二条路,或者相反。从这个顶点的第三条路到第四条路或者相反。
可以在顶点i上花费ci的费用令四条路重新排序;
求最小费用使得这张图连通。

思路:
已知这张图上有许多个闭环,可以在顶点上花费费用使得两个环合并,那么我们只用先使用并查集令原先的路合并,再用最小生成树即可。

int n,m;
int w[100060];
int s[5][100070];
int fa[400060];
int find(int x){
	if(fa[x]==x)return x;
	return fa[x]=find(fa[x]);
}
void join(int x,int y)
{
	x=find(x),y=find(y);
	if(x==y)return;
	fa[y]=x;
}
struct node{
	int from,to,cost;
}z[maxn];
vector<int>P[10*maxn];
int cnt=0;
bool cmp(node a,node b){
	return a.cost<b.cost;
}
int main(){
	scanf("%d",&n);
	
	for(int i=1;i<=4*n;i++)fa[i]=i;
	
	for(int i=1;i<=n;i++){
		scanf("%d%d%d%d%d",&w[i],&s[1][i],&s[2][i],&s[3][i],&s[4][i]);
		join(s[1][i],s[2][i]);
		join(s[4][i],s[3][i]);
	}
	for(int i=1;i<=n;i++){
		int id1=s[1][i],id2=s[3][i];
		if(find(id1)!=find(id2)){
			++cnt;
			z[cnt].from=find(id1);
			z[cnt].to=find(id2);
			z[cnt].cost=w[i];
		}
	}
	sort(z+1,z+1+cnt,cmp);
	int res=0;
	for(int i=1;i<=cnt;i++){
		if(find(z[i].from)!=find(z[i].to)){
			res+=z[i].cost;
			join(z[i].from,z[i].to);
		}
	}
	printf("%d\n",res);
	return 0;
}

原本想以城市为顶点,这样城市就要拆点了,挺麻烦的;
另一种思路就是合并道路,因为环的基本要素也是道路,最后 的生成树也是合并道路,从这个点出发,那就只需要合并道路就好了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值