poker P3793

Description

小F和小Z正在玩扑克牌。他们的扑克牌非常奇怪,正反两面都印有数字,分别是a[i],b[i]。一开始,桌面上摆着n张扑克牌。这个游戏一共进行n-1轮,每一轮他们可以选择两张扑克牌i,j,然后从中丢弃一张,剩下的一张放回桌面上。那么这一轮中他们的得分为min(a[i]b[j],a[j]b[i]).其中^表示异或。现在,小 F和小Z想知道这个游戏最少能拿多少分呢?


Input

每个测试点包含多组数据。输入数据的第一行为C,为数据个数。每组数据的第一行为n;接下来一行n个正整数表示每张牌正面的数字,接下来一行n个正整数表示每张牌反面的数字。


Output

每组数据输出一行,为这个游戏的最小得分。


Hint

样例解释:对于第二组数据,消除方案是,第一次选择第一张和第二张牌,扔掉第二张,得分为 3,第二次选择剩下的两张牌,得分为 2。


数据规模与约定:对于20%的数据,n<=10。对于40%的数据,n<=20对于 70%的数据,n<=100对于100%的数据,有1<=n<=3000,一个测试点中所有n的和<=10^4.


Solution

这道题乍一看觉得是kruskal但是如果仔细看一下Hint就会发现其实用kruskal会 超 时(kruskal的时间复杂度是(m*logm,对于稀疏图适用)。And对于这道题,他是个稠密图,所以适用prim计算最小生成树(prim的时间复杂度是n^2),所以就没了。


注意事项:
1.只需要memset(vis,false,sizeof(vis));因为其他的变量已经在init()或者prim()的过程中更新了。
2.只有一条看起来不好看。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define maxn 3005
#define inf 0x3f3f3f3f
using namespace std;
struct data{
	int leftt;
	int rightt;
}cardd[maxn];
int g[maxn][maxn],minn[maxn],minp,minw;
bool vis[maxn];
int n,T;
long long sumv;
void Clear(){
	//memset(g,inf,sizeof(g));
	memset(vis,false,sizeof(vis));
	//memset(minn,inf,sizeof(minn));
	minp=minw=sumv=n=0;
	//memset(cardd,0,sizeof(cardd));
}
void init(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&cardd[i].leftt);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&cardd[i].rightt);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			g[i][j]=min(cardd[i].leftt^cardd[j].rightt,cardd[i].rightt^cardd[j].leftt);
		}
	}
}
void prim(){
	for(int i=1;i<=n;i++){
		minn[i]=g[1][i];
	}
	vis[1]=true;
	minn[1]=0;
	for(int i=1;i<n;i++){
		minw=inf;
		for(int j=1;j<=n;j++){
			if(minw>minn[j]&&!vis[j]){
				minp=j;
				minw=minn[j];
			}
		}
		vis[minp]=true;
		sumv+=minw;
		for(int j=1;j<=n;j++){
			minn[j]=min(minn[j],g[minp][j]);
		}
	}
}
int main(){
	scanf("%d",&T);
	for(int i=1;i<=T;i++){
		Clear();
		init();
		prim();
		printf("%lld\n",sumv);
	}
	return 0;
}
posted @ 2018-11-30 17:17  虚拟北方virtual_north。  阅读(135)  评论(0编辑  收藏  举报