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;
}