山东冬令营2018:贪心专练
T1:
数轴上有 n 个点,第 i 个点的坐标为 xi,权值为 wi。两个点 i,j 之间存在一条边当且仅当 abs(xi-xj)>=wi+wj。 你需要求出这张图的最大团的点数。 (团就是两两之间有边的顶点集合) 【输入格式】 输入文件clique.in 第一行一个整数 n,接下来 n 行每行两个整数 xi,wi。 【输出格式】 输出文件clique.out 输出一行一个整数,表示最大团的点数。 【样例输入】 4 2 3 3 1 6 1 0 2 【样例输出】 3 【数据范围】 对于 20%的数据,n<=10。 对于 60%的数据,n<=1000。 对于 100%的数据,n<=200000,0<=|xi|,wi<=10^9。
本弱看到这道题时第一反应肯定是n^2连边也过不了啊,所以肯定有巧妙的方法来解这道题废话我们可以看到,有边相连的2个点的要求是abs(xi-xj)>=wi+wj,也就是说如果i和j有边,那么从xi-wi到xi+wi和xj-wj到xj+wj这两段区间是没有重合部分的,那么我们把每个点都扩成一段区间,最多的不重合的区间数就是最大团的点数,因为互不重合就代表了两两之间有边。
代码:
1 #include<algorithm> 2 #include<iostream> 3 #include<cstdio> 4 using namespace std; 5 const int maxn=200004; 6 int n; 7 struct zhw{ 8 int l,r; 9 friend bool operator <(zhw a,zhw b) 10 { 11 return a.r<b.r||(a.r==b.r&&a.l>b.l); 12 } 13 }a[maxn]; 14 int x,w,l,r; 15 int main() 16 { 17 freopen("clique.in","r",stdin); 18 freopen("clique.out","w",stdout); 19 scanf("%d",&n); 20 for(int i=1;i<=n;++i) 21 { 22 scanf("%d%d",&x,&w); 23 a[i].l=x-w,a[i].r=x+w; 24 } 25 sort(a+1,a+n+1); 26 int pos=a[1].r,ans=1; 27 for(int i=1;i<=n;++i) 28 { 29 if(a[i].l>=pos)ans++,pos=a[i].r; 30 } 31 printf("%d",ans); 32 fclose(stdin);fclose(stdout); 33 return 0; 34 }
T2:
小 H 参加了一场神秘的游戏。游戏中有 n 堆硬币,第 i 堆价值 ai。 每次小 H 可以选择编号相差 k 的硬币同时拿走。注意拿走后硬币不进行重标号。 小 H 想知道最多能拿走多大价值的硬币。 【输入格式】 输入文件coin.in 第一行两个整数 n,k。 第二行 n 个整数。第 i 个整数表示 ai。 【输出格式】 输出文件coin.out 一行一个整数,表示拿走硬币的最大价值。 【样例输入】 7 3 7 5 8 6 4 3 2 【样例输出】 33 【数据范围】 对于 20%的数据,n<=20。 对于 40%的数据,n<=2000。 对于另外 20%的数据,k<=10。 对于 100%的数据,n<=100000,k<=n,0<=ai<=1000000000。
本弱的脑回路有点怪(其实就是弱的做不粗来),想了半天的给可以同时拿走的数之间连边,也没搞出来,最后去看的题解(捂脸)。思路如下:既然可以两组队的拿走,那么对于膜k相等的数里就最多只有1个数拿不走,就是当这些数有奇数个的时候,有一个拿不走,并且这个一定是在奇数位置的,就是把所有的膜k相等的数按原来的顺序排成一列,奇数位置上的数,所以只要把这些位置上的数取min,然后把这个最小的数减掉就可以了,不过如果一共有偶数个就不需要减了,因为都可以拿走。
代码:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int maxn=100003; 5 long long a[maxn]; 6 int n,k; 7 long long ans; 8 int main()// 9 { 10 // freopen("coin.in","r",stdin); 11 // freopen("coin.out","w",stdout); 12 scanf("%d%d",&n,&k); 13 for(int i=1;i<=n;++i)scanf("%lld",&a[i]); 14 for(int i=1;i<=k;++i) 15 { 16 int tot=0,Min=2147483640;//极大值开小了 17 for(int j=i;j<=n;j+=k) 18 { 19 tot^=1,ans+=a[j]; 20 if(tot)Min=Min>a[j]?a[j]:Min; 21 } 22 ans-=tot*Min; 23 } 24 printf("%lld",ans); 25 return 0; 26 }