牛客练习赛 43 CTachibana Kanade Loves Review 线性做法,贪心(Java版成功)

链接:https://ac.nowcoder.com/acm/contest/548/C
来源:牛客网

题目描述
立华奏是一个刚刚开始学习 OI 的萌新。
最近,实力强大的 Qingyu 当选了 IODS 9102 的出题人。众所周知, IODS 是一场极其毒瘤的比赛。为了在这次比赛中取得好的成绩,立华奏决定学习可能考到的每一个知识点。
在Qingyu 的博客中,立华奏得知这场比赛总共会考察选手 n 个知识点。此前,立华奏已经依靠自学学习了其中 k 个知识点。接下来,立华奏需要学习其他的知识点,每学习一个单独的知识点,需要消耗的时间为 Ti 天。同时,某些知识点之间存在联系,可以加速学习的过程。经过计算,立华奏一共发现了其中 m 种联系,第 i 种联系可以表示为(Xi,Yi,Hi),其含义为“在掌握了第 Xi 个知识点和第 Yi 个知识点中任意一个后,学习 Hi 天即可掌握另一个知识点”。
留给立华奏的时间所剩无几,只有 t 天,因此,她想知道自己能不能在这 t 天内学习完成所有的知识点。
输入描述:
本题输入量较大,请注意使用效率较高的读入方式
输入的第一行包含四个整数 n, m, k, t,含义见上所述。
接下来一行,包含 n 个整数,依次表示 T1,T2,⋯,Tn
接下来一行,包含 k 个整数,表示立华奏已经学习过的知识点。如果 k=0,则此处为一空行。
接下来 m 行,每行 3 个整数 Xi,Yi,Hi,描述一种联系。
输出描述:
如果立华奏能够学习完所有的知识点,输出一行 Yes。否则输出 No
示例1
输入
4 3 2 5
4 5 6 7
2 3
1 2 3
1 3 2
3 4 2
输出
Yes
说明
立华奏已经学习过了第 2, 3 个知识,由第 2 个关系,立华奏可以花 2 天学会知识点 1,在由关系 3, 立华奏可以 2 天学会知识点 4,因此总共需要花费 4 天,可以完成任务。
示例2
输入
5 4 0 12
4 5 6 7 1

1 2 3
1 3 2
3 4 2
1 5 233
输出
Yes
说明
立华奏比较菜,因此什么都没有学过。她可以选择先花 4 天的时间学会知识点 1。然后根据关系 1, 2,分别花 3, 2 天的时间学会知识点 2, 3,再根据关系 3,花 2 天的时间学会知识点 4。然后,她再单独学习知识点 5,花费1天,总共花费了 12 天 ,可以完成任务。

请注意,虽然关系 4 允许立华奏在知识点 1 的基础上学习知识点 5,但需要的时间比单独学习还要多,因此立华奏不会在知识点 1 的基础上学习知识点 5.
备注:
0⩽k⩽n⩽10^6,
m⩽5×10^6,
t⩽10^18,
Ti,Hi⩽10^3

思路:用最小生成树的思路失败了。。Java太耗时了

第二种线性做法,先按最坏考虑,把所有时间加上去,然后学过的就减去这个时间,然后在一个
一个联系中把最优解算出,如果出现了总时间消耗已经小于目标时间就输出yes结束,
如果已经通过所有联系都没法使总时间消耗小于目标时间就结束循环跳出输出no
这种做法是看通过代码中耗时最短的发现的,最小生成树的做法Java过不去。。
终于用Java成功ac这题:耗时比想象中大。
在这里插入图片描述
c通过的我是用最小生成树,用了快读,然而通过代码中最快的c只用200多ms,用的就是我Java通过的方法,还没用快读。。可以想象这差了几十倍的速度啊。难怪我用最小生成树的方法Java过不去。。
代码:


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

//第二种线性做法,先按最坏考虑,把所有时间加上去,然后学过的就减去这个时间,然后在一个
//一个联系中把最优解算出,如果出现了总时间消耗已经小于目标时间就输出yes结束,
//如果已经通过所有联系都没法使总时间消耗小于目标时间就结束循环跳出输出no
//这种做法是看通过代码中耗时最短的发现的,最小生成树的做法Java过不去。。
public class Main {
	public static void main(String[] args) throws IOException {
		StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		int n,m,k;
		long t,sum=0;
		st.nextToken();
		n=(int)st.nval;
		st.nextToken();
		m=(int)st.nval;
		st.nextToken();
		k=(int)st.nval;
		st.nextToken();
		t=(long)st.nval;
		int tn[]=new int[n+1];
		for(int i=1;i<=n;i++){
			st.nextToken();
			tn[i]=(int)st.nval;			
			sum+=tn[i];		//单独学习每一门的时间加起来,相当于是最坏的情况	
		}	
		while(k-->0){
			st.nextToken();
			int temp=(int)st.nval;
			sum-=tn[temp];  //如果这门已经学会了,就减去这个时间
			tn[temp]=0;  //这一门的权值变为0
		}	
		int x,y,h;
		for(int i=0;imax)
				max=tn[x];
			if(tn[y]>max)
				max=tn[y];
			if(h>max)
				continue;//如果通过联系来学习的代价大于两个知识点中的最大值,说明没必要通过联系来学习知识点
			if(tn[x]>tn[y]){//让两个知识点中代价大的权值通过联系变为较小的h
				tn[x]=h;
			}else{
				tn[y]=h;
			}
			sum-=max; //计算通过联系优化之后的总耗时
			sum+=h;
			if(sum<=t){ //如果经过k次联系之后sum已经小于等于t了,就不用继续求下去了,因为sum只会越来越小
				System.out.println("Yes");
				return;
			}
		}
		System.out.println("No");
	}
}

你可能感兴趣的:(牛客)