cogs1355. 读书
1355. 读书
★ 输入文件:reading.in
输出文件:reading.out
简单对比
时间限制:1 s 内存限制:128 MB
【题目描述】
放暑假了,CHH想趁假期提高一下自己的计算机水平,于是他从学校图书馆借了N本计算机科学方面的书,这N本书的编号依次为0~N-1。
读完第i本书,CHH需要花费time[i]分钟,但是有一些书的内容是相近的,如果第i本书与第j本书内容相近,那么如果CHH读完了第i本书,再读第j本书的时候只需要floor(time[j]/2)分钟的时间即可(其中floor()表示对括号中的式子进行下取整);当然,如果CHH先读完了第j本书,那么再读第i本书的时候只需floor(time[i]/2)的时间。现在你的任务是告诉CHH,他最少可以用多少分钟读完这N本书。
【输入格式】
第一行有两个整数N(0<=N<=100)和M(0<=M<=N(N-1)/2)。N为书的总数,有M对书内容相近。
接下来有N行,分别表示time[0],time[1],...及time[N-1],(1<=time[i]<=10^5)。
再接下来有M行,每行有两个整数(i,j),表示第i本书与第j本书内容相近。
输入文件以N=0,M=0表示结束。
【输出格式】
对于每组测试数据,输出仅一行,即最少时间。
【样例输入】
2 1 6 10 0 1 3 2 1 2 3 0 1 1 2 3 1 2 4 6 0 1 0 0
【样例输出】
11 3 10
【提示】
对于第一组数据,如果CHH读的顺序为(0,1),则总的时间为6+10/2=11,如果读的顺序为(1,0),则总的读书时间为10+6/2=13.
思路:
题目中提及:
两本书之间可能内容相近,所以我们可以采用并查集,然后贪心选一个集合中用时最小的书,剩下的除2加入总时间即可。
故我们可以用kruskal来做这道题
坑点:
1)若出现m==0 但是 n!=0,此时不要退出程序!!!
2)要从0开始进行标号
上代码:
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> using namespace std; const int N = 110; const int M = 5050; int n,m,ans; int t[N],dad[N]; bool vis[N]; int getdad(int x) {return dad[x] == x ? x : dad[x] = getdad(dad[x]);} void kruskal() { while(true) { int flag=-1; int tmp[200]={0},cnt=0; for(int i=0;i<n;i++) { if(flag==-1 && !vis[i]) { vis[i]=true; flag=getdad(i); tmp[++cnt]=t[i]; } else if(flag!=-1 && !vis[i] && getdad(i)==flag) { vis[i]=true; tmp[++cnt]=t[i]; } } if(!cnt) { cout<<ans<<endl; break; } sort(tmp+1,tmp+1+cnt); ans+=tmp[1]; for(int i=2;i<=cnt;i++) ans+=floor(tmp[i]>>1); } } void chu() { ans=0; memset(t,0,sizeof(t)); memset(vis,false,sizeof(vis)); memset(dad,0,sizeof(dad)); } int main() { freopen("reading.in","r",stdin); freopen("reading.out","w",stdout); while(cin>>n>>m) { if(n==0 && m==0) break; chu(); for(int i=0;i<n;i++) cin>>t[i],dad[i]=i; for(int i=0,u,v;i<m;i++) { cin>>u>>v; int f1=getdad(u),f2=getdad(v); if(f1!=f2) dad[f1]=f2; } kruskal(); } return 0; }