1. 程式人生 > >山東冬令營2018:貪心專練

山東冬令營2018:貪心專練

print AI 最大團 區間 con div err algorithm close

  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<=2000000<=|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 }
View Code

  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 }
View Code

  T3:

有n個櫻桃排成一列,第i個櫻桃的甜度為v[i],你要把n個櫻桃分成若幹組,其中每一組的櫻桃必須相鄰。每一組櫻桃的美味度為(sum-T)^2 , 其中sum是這組櫻桃的甜度之和,T為輸入給定的系數。
一組方案的美味度為每一組的美味度之和。
求可行方案最小的美味度。
【輸入格式】
輸入文件cherry.in
第一行兩個正整數 n,T。
第二行n個整數,第i個整數是第i個櫻桃的甜度,v[i]。
【輸出格式】
輸出文件cherry.out
一行一個非負整數,為最小美味度。
【樣例輸入】
5 5
3 5 2 1 6
【樣例輸出】
9
【數據範圍】
對於50%的數據滿足 n<=10,T<=1000,v[i]<=10
對於70%的數據滿足 n<=100
對於所有數據,n<=10^3,T<=1000,v[i]<=10

  看到數據範圍肯定會想起是不是n^2做法啊,其實這題大概是dp,f[i]代表前i個櫻桃分成若幹組的最小美味度,那麽有轉移方程:f[i]={min(f[i],sum[i]-sum[j-1]-T)^2) | j<=i},然後就皆大歡喜啦。

代碼:

技術分享圖片
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 const int maxn=1005;
 6 int n,t;
 7 int v[maxn],sum[maxn];
 8 int f[maxn];
 9 int main()
10 {
11 //    freopen("cherry.in","r",stdin);
12 //    freopen("cherry.out","w",stdout);
13     scanf("%d%d",&n,&t);
14     for(int i=1;i<=n;++i)scanf("%d",&v[i]),sum[i]=sum[i-1]+v[i];
15     memset(f,127/3,sizeof(f));
16     f[0]=0;
17     for(int i=1;i<=n;++i)
18         for(int j=1;j<=i;j++)
19             f[i]=min(f[i],f[j-1]+(sum[i]-sum[j-1]-t)*(sum[i]-sum[j-1]-t));
20     printf("%d",f[n]);
21     return 0;        
22 } 
View Code

山東冬令營2018:貪心專練