[SinGuLaRiTy] 貪心題目復習


【SinGuLaRiTy-1024】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.

[POJ 2709] 顏料 (Painter)

題目描述

雜貨店出售一種由N(3<=N<=12)種不同顏色的顏料,每種一瓶(50ML),組成的顏料套裝。你現在需要使用這N種顏料;不但如此,你還需要一定數量的灰色顏料。雜貨店從來不出售灰色顏料——也就是它不屬於這N種之一。幸運的是,灰色顏料是比較好配置的,如果你取出三種不同顏色的顏料各x ml,混合起來就可以得到x ml的灰色顏料(注意不是3x)。

現在,你知道每種顏料各需要多少ml。你決定買盡可能少的“顏料套裝”,來滿足你需要的這N+1種顏料。那么你最少需要買多少個套裝呢?

輸入

輸入包含若干組測試數據。每組數據一行:第一個數N, 3<=N<=12, 含義如上;接下來N+1個數,分別表示你需要的N+1種顏料的毫升數。最后一種是灰色。所有輸入的毫升數<=1000.

注意:輸入中不存在每個顏料套裝的毫升數。由題意可知,每種各50ml,即一共50N ml。

輸出

對於每一組數據,輸出一個整數,表示最少套數。

樣例數據

樣例輸入 樣例輸出
3 40 95 21 0
7 25 60 400 250 0 60 0 500
4 90 95 75 95 10
4 90 95 75 95 11
5 0 0 0 0 0 333
0
2
8
2
3
4


 

 

 

 

 

 

解析

樣例的前四組數據,都很簡單,直接找最大,然后模擬就可以,沒什么好說的。但對於第五組數據,就有點坑了。我們會發現:如果按照常規解法一次50ml地配灰色顏料,答案顯然不是4.因此,在這里我們要1ml、1ml地配,直到配滿灰色的為止。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=15;
int a[maxn],b[maxn];
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
int n,m,k;
while(scanf("%d",&n)&&n)
{
for(int i=0;i<n;i++)
scanf(
"%d",&a[i]);
scanf(
"%d",&m);
sort(a,a
+n);
if(a[n-1]%50==0) k=a[n-1]/50;
else k=a[n-1]/50+1;
int t=0;
for(int i=0;i<n;i++)
b[i]
=k*50-a[i];
while(t<m)
{
if(b[2]==0)
{
k
+=1;
for(int i=0;i<n;i++)
b[i]
+=50;
}
else
{
t
++;
b[
0]--,b[1]--,b[2]--;
sort(b,b
+n,cmp);
}
}
cout
<<k<<endl;
}
return 0;
}

[POJ 2393] 酸奶制造廠 (Yogurt Factory)

題目描述

任務規定,一個酸奶制造廠,在N (1 <= N <= 10,000) 個星期內,分別要向外提供Y[i] (0 <= Y_i <= 10,000)單位的酸奶。已知這個制造廠第i周制造每單位酸奶的費用為C[i] (1 <= C_i <= 5,000),儲存室儲存每1單位酸奶1星期的費用為S (1 <= S <= 100) 。問要完成這個任務的最小費用是多少。

輸入

第一行:兩個整數N和S

第2~n+1行:第i+1行包含兩個整數C[i]和Y[i]

輸出

一個整數,表示滿足安排的最小花費。注意:這個數可能會超過32位整型。

樣例數據

樣例輸入 樣例輸出
4 5
88 200
89 400
97 300
91 500
126900

 

 

 

 

 

解析

貪心。對於每一周的訂單,維護最小花費:min_cost=min(C[i],min_cost+S)。

Code

#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<iostream>

#define LL long long
#define MAXN 10010
#define INF 0x3f3f3f3f

using namespace std;

LL C[MAXN];
LL Y[MAXN];
LL cost;

int main()
{
int MAX=INF;
int N,S;
scanf(
"%d%d",&N,&S);
for(int i=1;i<=N;i++)
{
cin
>>C[i]>>Y[i];
if(C[i]>MAX+S)
C[i]
=MAX+S;
MAX
=C[i];
cost
+=C[i]*Y[i];
}
cout
<<cost;
return 0;
}

[POJ 1877] 淹沒 (Flooded!)

題目描述

將一個區域分成r*c個方塊,每個方塊有有一個海拔(可正可負)。求當給區域注入指定容量的水時,水面的海拔是多少,以及被水淹沒的方塊占總方塊數的百分比。每個方塊的面積為100m^2,水的容量單位為立方米。 

輸入

多組用例,每組用例第一行為兩個整數r和c表示區域行列數,然后是一個r*c的矩陣,矩陣元素為對應區域方塊的海拔,最后是注入水的體積,以r=c=0結束輸入 

輸出

對於每組用例,輸出注入水后水面的海拔以及被水淹沒方塊占總方塊數的百分比 

樣例數據

樣例輸入 樣例輸出
3 3
25 37 45
51 12 34
94 83 27
10000
0 0
Region 1
Water level is 46.67 meters.
66.67 percent of the region is under water.

 

 

 

 

 

 

解析

(Tips: 如果在POJ上一直莫名TLE,WA,RE,建議把默認語言的G++換成C++,玄學......)

看到這道題,首先要明白這個r*c的區域和矩陣一點關系都沒有,於是果斷改成一列的一維數組。此時又發現體積並不好算,於是來一個sort,此時的區域就變成了如圖-1所示的樣子。現在,問題就比較簡單了:對於每一組數據,我們只需要判斷水面從左到右延伸到了哪一個位置就能判斷覆蓋方塊數了(在每一個位置都有一個體積范圍)。而對於體積,運用小學的體積公式也就搞出來了。(注意浮點數精度誤差)

                                      圖-1

Code

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;

int n,m,T;
int i;
double v;
double land[1000];

int main()
{
T
=1;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(m==0&&n==0)
return 0;
for(i=0;i<n*m;i++)
cin
>>land[i];
cin
>>v;
sort(land,land
+n*m);
for(i=0;i<n*m-1;i++)
{
if(v<(land[i+1]-land[i])*(i+1)*100)
break;
v
-=(land[i+1]-land[i])*(i+1)*100;
}
double h=land[i]+v/(i+1)/100;
if(v==0)
h
=1;
printf(
"Region %d\n",T++);
printf(
"Water level is %.2f meters.\n",h);
printf(
"%.2f percent of the region is under water.\n",(i+1)/((double)(n*m))*100);
printf(
"\n");
}
return 0;
}

[POJ 1328] 雷達安裝 (Radar Installation)

題目描述

我們假設海岸是無限延伸的直線,陸地在海岸的一邊,大海在另一邊。每個小島是一個點位於大海那一片區域。詳見圖片; 每一個雷達的安裝位置只能夠在海岸線上,只能夠覆蓋半徑為 d 的圓形區域。所以,如果小島與雷達距離小於等於d才能夠覆蓋。 我們可以用笛卡爾積坐標系,定義海岸線是 x軸,大海在x 上方,陸地在x 軸下方,給你每個小島在大海中的位置,並且給出雷達的覆蓋范圍 d ,你的任務是寫一個程序,找到最少需要安裝的雷達數量且能夠覆蓋所有的小島。一個位置表示(x,y)坐標。

     Figure A Sample Input of Radar Installations

輸入

輸入是多組語句輸入; 每行包含兩個數 n 和 d:n 代表小島的數量,d 代表雷達的覆蓋半徑; 接下來n行是小島的位置,用一個二維坐標來表示Xi,Yi; 當輸入的 n 和 d 都為0,程序結束。 

輸出

對於每組數據輸出一個答案,每個答案占一行,輸出最小需要的雷達數量,如果不能夠滿足要求,則輸出 -1。

樣例數據

樣例輸入 樣例輸出
3 2 1 2 -3 1 2 1   1 2 0 2   0 0  Case 1: 2 Case 2: 1

 

 

 

 

 

 

 

 

解析

首先,我們將島嶼從左到右(x坐標從小到大)進行排序。接下來,我們走一遍循環,計算出能覆蓋第i個島嶼的雷達的坐標范圍 [ll[1],rr[1]] (相當於是x軸上的線段) ,如圖-2。接下來,我們從左到右再走一次循環,這一次,我們看這些一段段線段有多少重合區域(這意味着有些島嶼可共用一個雷達),最后統計,求出答案。

                             圖-2

Code

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>

using namespace std;
double x[10000],y[10000],ll[11000],rr[10000];
int main()
{
int n,d;
int cnt=1;
while(cin>>n>>d)
{
if(n==0&&d==0)
break;
int flag=1,sum=1;
for(int i=0;i<n;i++)
{
cin
>>x[i]>>y[i];
if(y[i]>d)
flag
=0;
}
cout
<<"Case "<<cnt;
cnt
++;
if(flag==0||d==0)
{
cout
<<": -1"<<endl;
continue;
}
int t;
for(int i=0;i<n-1;i++)
{
for(int j=0;j<n-i-1;j++)
{
if(x[j]>x[j+1])
{
t
=x[j];
x[j]
=x[j+1];
x[j
+1]=t;
t
=y[j];
y[j]
=y[j+1];
y[j
+1]=t;
}
}
}
for (int i=0;i<n;i++)
{
double s=sqrt(d*d-y[i]*y[i]);
ll[i]
=x[i]-s;
rr[i]
=x[i]+s;
}
double x=rr[0];
for(int i=1;i<n;i++)
{
if(ll[i]>x)
{
x
=rr[i];
sum
++;
}
if(rr[i]<x)
{
x
=rr[i];
}
}
cout
<<": "<<sum<<endl;
}
return 0;
}

[POJ 1018] 通訊系統 (Communication System)

題目描述

Pizoor Communications Inc.要建立一套通信系統,該通信系統需要n種設備,而每種設備分別可以有m1、m2、m3、...、mn個廠家提供生產,而每個廠家生產的同種設備都會存在兩個方面的差別:帶寬bandwidths 和 價格prices。

現在每種設備都各需要1個,考慮到性價比問題,要求所挑選出來的n件設備,要使得B/P最大。其中B為這n件設備的帶寬的最小值,P為這n件設備的總價。

輸入

第一行包含一個整數t (1 ≤ t ≤ 10),表示有t組數據。

每組數據的第一行包含一個整數n (1 ≤ n ≤ 100),表示所需的設備種類數。接下來有n行,其中第i (1 ≤ i ≤ n)行的第一個整數mi (1 ≤ mi ≤ 100)表示生產第i種設備的廠家數量,接下來在同一行有兩兩成對的整數,第一個表示帶寬,第二個表示價格。

輸出

輸出對於每一組數據而言的B/P的最大值,保留三位小數。

樣例數據

樣例輸入 樣例輸出
1 3
3 100 25 150 35 80 25
2 120 80 155 40
2 100 100 120 110
0.649

 

 

 

 

 

解析

本來聽說這道題是一道DP題,但考慮到這是貪心復習,還是用貪心+剪枝吧。

從最小帶寬開始枚舉,用貪心法選出帶寬大於等於最小帶寬的最低價格;然后再比較更新最大的(B/P)的值,直到到達最大帶寬;如果從帶寬枚舉上去,一定符合要求。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;

const int Max=101;

struct node
{
double b;
double p;
};
node a[Max][Max];

double b[Max*Max];
int m[Max];
int bsize;

int cmp(const void *a,const void *b)
{
return (*(double *)a)-(*(double *)b);
}

int main()
{
int t,n;
cin
>>t;
while (t--)
{
memset(a,
0,sizeof(a));
memset(m,
0,sizeof(m));
cin
>>n;
int i,j,k;
int bsize=0;
for (i=0;i<n;i++)
{
cin
>>m[i];
for (j=0;j<m[i];j++)
{
cin
>>a[i][j].b>>a[i][j].p;
b[bsize
++]=a[i][j].b;
}
}
qsort(b,bsize,
sizeof(b[0]),cmp);
double mmax=0;
double mmin;
double sump=0;
double temp=0;
for (i=0;i<=bsize-1;i++)
{
sump
=0;
int changFlag = 0;
for (j=0;j<n;j++)
{
mmin
=32767;
for (k=0;k<m[j];k++)
{
if (a[j][k].b>=b[i]&&a[j][k].p<mmin)
{
mmin
=a[j][k].p;
changFlag
= 1;
}
}
sump
+=mmin;
}
if(!changFlag)
break;
temp
=b[i]*1.0/sump;
if(temp>mmax)
mmax
=temp;
}
printf(
"%.3lf\n",mmax);
}
return 0;
}

 

[POJ 1083] 移動桌子 (Moving Tables)

題目描述

ACM公司租用的樓層如下圖所示:

這層樓的兩端各有200個房間。現在公司決定在房間之間轉移桌子。在移動過程中,有且僅有一張桌子能夠在過道內。因此,ACM公司需要一種高效的安排來搬運桌子。已知,將一張桌子從一個房間移到另一個房間需要不到10分鍾。由於將桌子從房間i到房間j移動的過程中,僅需使用過道中的i~j的部分。若兩個桌子的移動不共同使用同一段過道,就可以同時進行。例如下表所示:

對於每一個房間而言,僅有一張桌子可以被移進或移出。你的任務是寫一個程序來幫助計算所需的最少時間。

輸入

輸入包含T組數據。

對於每一組數據,第一行包含一個整數N (1 <= N <= 200),表示需要移動的桌子。接下來有N行,每行兩個整數s和t,表示桌子從s移動到t。

輸出

對於每一組數據,輸出完成移動所需的最少時間。

樣例數據

樣例輸入 樣例輸出
3 
4
10 20
30 40
50 60
70 80
2
1 3
2 200
3
10 100
20 80
30 50
10
20
30

 

 

 

 

 

 

 

 

 

 

 

解析 

只需把區間經過的地方計數,最后找到最大計數就可以了。答案的最優性說明:題意可以用區間圖表示,即每一個區間為一個頂點,如果兩個區間范圍相交,則在其間連上一條邊。題目便轉化為求區間圖的色數,由定理:區間圖中色數=團數知道求最大團的頂點數即可。那么最大團的頂點數就是上述思路。[最大團是什么?]

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

#define N 202

using namespace std;

int n,flag[N],T;

int main()
{
scanf(
"%d",&T);
while(T--)
{
int a,b,res=0;
scanf(
"%d",&n);
memset(flag,
0,sizeof(flag));
for(int i=0;i<n;i++)
{
scanf(
"%d %d",&a,&b);
if(a>b)
{
swap(a,b);
}
a
=(a-1)>>1;
b
=(b-1)>>1;
for(int j=a;j<=b;j++)
flag[j]
++;
}
for(int i=0;i<N;i++)
res
=max(res,flag[i]);
printf(
"%d\n",res*10);
}
return 0;
}

[HDU 1239] 再次呼叫地外文明(Calling Extraterrestrial Intelligence Again)

題目描述

給出一個大於4的整數m和一個真分數a/b,求最佳素數對p、q,使得a/b<=p/q<=1且pq<=m。最佳即為滿足條件的數對中pq最大的一對。

輸入

輸入文件最多2000行。每一行包含了三個整數:整數m,分子a,分母b。( 4 < m <= 100000 and 1 <= a <= b <= 1000 且滿足 0 < a / b < 1)

以三個0結尾。

輸出

輸出是一個由正整數數對組成的序列。表示每一組數據中的p、q。

樣例數據

樣例輸入 樣例輸出
5 1 299999 999 9991680 5 161970 1 12002 4 110 0 0 2 2313 31323 7343 4337 53

 

 

 

 

 

 

解析

直接枚舉查找:快速求出素數表后,運用二分查找檢查當前數是否為素數。(注意,由於是求最大,所以從后往前搜,直到j<z時退出)。

還要關注p的取值范圍:[1,sqrt(n)] 

Code

#include<cstdio>
#include<cmath>
#include<cstring>

using namespace std;

int prim[100005],is[100005];
int num=0;

int Find(int v)
{
int m;
int l=1,h=num;
while(l<h)
{
m
=(l+h)/2;
if(prim[m]==v)
return 1;
if(prim[m]<v)
l
=m+1;
else
h
=m;
}
return 0;
}

int main()
{
int m,a,b,Max,j,i,x,zx,zi,z;
double t;
int s,e=(int)(sqrt(0.0+100000)+1);
memset(
is,1,sizeof(is));
prim[
++num]=2;
is[0]=is[1]=0;
for(i=4;i<=100000;i+=2)
is[i]=0;
for(i=3;i<e;i+=2)
if(is[i])
{
prim[
++num]=i;
for(s=i*2,j=i*i;j<=100000;j+=s)
is[j]=0;
}
for(;i<100000;i+=2)
if(is[i])
prim[
++num]=i;
while(scanf("%d%d%d",&m,&a,&b)!=EOF)
{
z
=0;
if(m==0 && a==0 && b==0)
break;
t
=a*1.0/b;
for(int j=m;j>=1;j--)
{
if(j<z)
break;
Max
=(int)sqrt(0.0+j);
for(i=Max;i>=1;i--)
{
x
=j/i;
if(x*t<=i && x>=i)
if(Find(i)&&Find(x))
if(x*i>z)
{
zx
=x;
zi
=i;
z
=x*i;
}
}
}
printf(
"%d %d\n",zi,zx);
}
return 0;
}

[UVA 1292] 戰略游戲 (Strategic Game)

題目描述

鮑勃喜歡玩戰略游戲。現在有一座城市,其內部的道路構成了一棵樹。鮑勃可以在某些城市派一個士兵守護,該士兵可以瞭望到所有與該城市相連的邊。問鮑勃最少要派遣多少個士兵,才能把所有的邊都瞭望到。

例如,在下面這棵樹中,只需要把士兵放在節點1就可以瞭望到左右的邊了。

 

輸入

輸入文件包含多組數據。
每一組數據包含了對樹的以下描述:
1> 節點數N;
2> 接下來N行,每一行對一個節點進行具體描述,其格式如下:
節點編號:(與其連接的道路數m) 連接的節點1 連接的節點2 連接的節點3 ......連接的節點m

輸出

對於每一組數據,輸出需要的最少士兵數。

樣例數據

樣例輸入 樣例輸出

4
0:(1) 1
1:(2) 2 3
2:(0)
3:(0)
5
3:(3) 1 4 2
1:(1) 0
2:(0)
0:(0)
4:(0)

1
2

 

 

 

 

 

 

 

 

 

解析

最少點覆蓋問題
可以用樹形DP解決,我們把無根樹抽象成一棵有根樹,0為樹根。對於任意一個節點i來說,設dp[i][0]表示在該節點不放士兵,dp[i][1]表示在該節點放置士兵。
那么結合他的子節點就可以得到狀態轉移方程
dp[i][1]=sum(dp[k][0])+1 k為i的子節點,下同,因為本節點沒放,則子節點一定要放
dp[i][0]=sum(min(dp[k][0],dp[k][1])) 因為本節點放了,所以取子節點放和不放的最小值
最后答案就是min(dp[0][0],dp[0][1])

Code

#include<iostream>  
#include<vector>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;

const int maxn = 1600;
int dp[maxn][2];
int n;
vector
<int> tree[maxn];

int min(int a,int b)
{
return a<b?a:b;
}

void dfs(int fa,int now)
{
dp[now][
0] = 0;
dp[now][
1] = 1;

int len = tree[now].size();
int i;

for(i=0;i<len;i++)
{
int t=tree[now][i];
if(t!=fa)
{
dfs(now,t);
dp[now][
0] += dp[t][1];
dp[now][
1] += min(dp[t][0],dp[t][1]);
}
}

}


int main()
{
while(~scanf("%d",&n))
{
int i;
for(i=0;i<n;i++)
{
tree[i].clear();
}
for(i=0;i<n;i++)
{
int b;
int a;
int j;
scanf(
"%d:(%d)",&a,&b);
for(j=0;j<b;j++)
{
int x;
scanf(
"%d",&x);
tree[a].push_back(x);
tree[x].push_back(a);
}
}
dfs(
-1,0);
cout
<<min(dp[0][0],dp[0][1])<<endl;
}
return 0;
}

[POJ 3617] 最佳奶牛序列 (Best Cow Line)

題目描述

農夫約翰帶着他的奶牛們去參加一個比賽。奶牛們排成一列,依次取出每頭奶牛名字的首字母組成隊名。為了得到一個字典序最小的隊名。約翰要重新排列奶牛隊伍,排列規則是這樣的:他只能將奶牛隊列的隊頭或隊尾依次加入新隊列的隊尾,直到所有奶牛全部加入到新隊列為止。問他能得到的字典序最小的隊名?

輸入

第1行包含一個整數N,表示奶牛的總數量。

第2到N+1行描述每一頭奶牛。第i+1行表示在原本序列中編號為i的奶牛的首字母。

輸出

字典序最小的字符串。

樣例數據

樣例輸入 樣例輸出
6
A
C
D
B
C
B
ABCBCD

 

 

 

 

 

 

 

解析

貪心就行。每次從上和從下選擇最小的,若相等則向中間搜索。

Code

#include<cstdio>
#include<cstring>
#include<iostream>

#define MAXN 2010

using namespace std;

int n,str[MAXN];

int main()
{
scanf(
"%d",&n);
char ch;
for(int i=0;i<n;i++)
{
getchar();
ch
=getchar();
str[i]
=ch-'A';
}
int pre=0,last=n-1,Count=0;
for(int i=0;i<n;i++)
{
if(str[pre]==str[last])
{
int pr=pre,la=last,flag=0;
while(pr<=la)
{
if(str[pr]<str[la])
{
printf(
"%c",str[pre]+'A');
Count
++;
flag
=1;
pre
++;
break;
}
else if(str[pr]>str[la])
{
printf(
"%c",str[last]+'A');
Count
++;
flag
=1;
last
--;
break;
}
pr
++,la--;
}
if(!flag)
{
printf(
"%c",str[pre]+'A');
Count
++;
pre
++;
}
}
else if(str[pre]<str[last])
{
printf(
"%c",str[pre]+'A');
Count
++;
pre
++;
}
else
{
printf(
"%c",str[last]+'A');
Count
++;
last
--;
}
if(Count==80)
{
Count
=0;
printf(
"\n");
}
}
if(Count)
printf(
"\n");
return 0;
}

[POJ 3069] 薩魯曼的軍隊 (Saruman's Army)

題目描述

薩魯曼要對自己的一列軍隊進行監視,因此他要賦予這一列軍隊中的部分士兵監視的權利。但每一個監視的士兵僅能覆蓋距離自己R個單位以內的士兵(包括第R個)。現在,若要覆蓋整支軍隊,至少需要多少個監視的士兵?

輸入

輸入包含多組數據。每一組數據以兩個整數R和N開始,R (0 ≤ R ≤ 1000)表示士兵能夠監視的范圍,N( 1 ≤ N ≤ 1000)表示這一列軍隊的士兵數。接下來一行包含N個整數,表示每一個士兵所處的位置X ( 1 ≤ X ≤ 1000)。當R=N=-1時,輸入數據結束。

輸出

對於每一組數據,輸出最少需要的士兵數。

樣例數據

樣例輸入 樣例輸出
0 3
10 20 20
10 7
70 30 1 7 15 20 50
-1 -1
2
4

 

 

 

 

 

 

解析

從一個未覆蓋的位置向前遍歷,找到滿足距離小於 R 的最右邊的點,這個點一定作為一個裝置的放置位置,然后從這個位置找到右邊的最小的不能覆蓋到的位置,這個位置作為下一次的起點...循環下去,直到所有的點都被覆蓋到。

Code

#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<iostream>

#define MAXN 1010

using namespace std;

int army[MAXN];

int R,N;

int main()
{
while(scanf("%d%d",&R,&N)!=EOF)
{
if(R==-1&&N==-1)
return 0;
for(int i=1;i<=N;i++)
{
scanf(
"%d",&army[i]);
}
sort(army
+1,army+N+1);
int i=1,cnt=0;
while(i<=N)
{
int bg=army[i++];
while(i<=N&&army[i]<=bg+R)
{
i
++;
}
int st=army[i-1];
while(i<=N&&army[i]<=st+R)
{
i
++;
}
cnt
+=1;
}
printf(
"%d\n",cnt);
memset(army,
0,sizeof(army));
}
}

[POJ 3253] 修理籬笆 (Fence Repair)

題目描述

Farmer John要去做籬笆,他一共需要N塊木板,也量出每塊木板需要多長。他買了這么一整塊木板,這個木板的長度是所有需要的木板長度之和。但是他沒有鋸子。於是他找朋友去借鋸子。可是朋友要收費。收費是這樣的,鋸開一塊長度為L的木板收費L元。請問Farmer John使用他朋友的鋸子,最少要花多少錢。

輸入

第1行: 包含一個整數N (1 ≤ N ≤ 20,000) ,表示所需木板的數量。
第2到N+1行:每行一個整數,表示一塊木板的長度L (1 ≤ L ≤ 50,000)。

輸出

一個整數,表示Farmer John最少花費的金額。

樣例數據

樣例輸入 樣例輸出
3
8
5
8
34

 

 

 

 

 

 

解析

由於要使花費最小,因此很容易知道,我們每一次需要切掉最長的那一個部分,來防止那一部分在后面的計算中被反復疊加進花費中。當然,如果我們直接用sort排序的話,肯定會超時,所以我們要用優先隊列。做這道題時總有種“合並果子”的感覺。

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>

using namespace std;

priority_queue
<int,vector<int>,greater<int> > pq;

int main()
{
int n(0);
cin
>>n;
while(cin>>n)
{
pq.push(n);
}
long long sum(0);
int l1(0),l2(0);
while(pq.size()>1)
{
l1
=pq.top();
pq.pop();
l2
=pq.top();
pq.pop();
sum
= sum+l1+l2;
pq.push(l1
+l2);
}
cout
<<sum;
return 0;
}

[POJ 2376] 打掃谷倉 (Cleaning Shifts)

題目描述

農夫約翰讓他的N頭奶牛們打掃谷倉。他把一天分成T個時間段,第一個時間段為1,最后一個時間段為T。第i頭奶牛願意從第Si個時間段工作到第Ti個時間段。問最少需要多少奶牛,才能保證每一個時間段都有奶牛在工作。如果不能保證,則輸出-1.

輸入

第1行:兩個整數N (1<= N <=25,000)和T (1<= T <=1,000,000)。
第2到N+1行:每一行包含一組開始時間和結束時間,表示一頭奶牛工作的時間段。

輸出

輸出一個整數,表示最少需要多少頭奶牛。

樣例數據

樣例輸入 樣例輸出
3 10
1 7
3 6
6 10
2

 

 

 

 

 

解析

這題乍一看,不就是區間覆蓋問題嗎? [CQBZOJ上的區間覆蓋問題] 

先對每一個時間段(其實就是線段)按左端點由小到大進行排序,從時間段1開始一段一段地接上來,連接時,在前后兩條線段有交點的前提下使新的右端點最遠,這樣就能保證使用最少的線段,即奶牛數最小。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

typedef
long long ll;

const int maxn = 25000 + 5;
const int maxd = 1000000 + 5;

struct Edge
{
int x;
int y;
}cow[maxn];

int N,T;
int X,Y;

bool cmp(Edge a,Edge b)
{
if(a.x==b.x) return a.y>b.y;
return a.x<b.x;
}

int main()
{
X
=0;
Y
=0;
scanf(
"%d%d",&N,&T);
for(int i=0;i<N;i++)
{
scanf(
"%d%d",&cow[i].x,&cow[i].y);
if(cow[i].x==1)
X
=1;
if(cow[i].y==T)
Y
=1;
}
sort(cow,cow
+N,cmp);
if(X==0||Y==0)
{
printf(
"-1\n");
return 0;
}
int ans=1,End=0;
int Start=cow[0].y;
int maxy=cow[0].y;
while(true)
{
while(End+1<N&&cow[End+1].x<=Start+1)
{
End
++;
if(cow[End].y>maxy)
{
maxy
=cow[End].y;
}
}
if(maxy!=Start)
{
ans
++;
Start
=maxy;
}
else
{
if(End==N-1)
{
break;
}
else
{
printf(
"-1\n");
return 0;
}
}
}
printf(
"%d\n",ans);
return 0;
}

[POJ 3190] 擠奶預訂 (Stall Reservation)

題目描述

有N頭奶牛,(1<=N<=50,000),每頭奶牛只在特定的時間段內[A,B]內才願意擠奶(1<=A,B<=1,000,000)。一個擠奶機只能同時擠一頭奶牛。農夫約翰最少需要准備多少個擠奶機,才能滿足所有奶牛的擠奶需求。

輸入

第1行:一個整數N,表示奶牛數。
第2到N+1行:每一行包含兩個整數,分別表示一頭奶牛願意擠奶的開始時間和結束時間。

輸出

第1行:輸出所需要的擠奶機的最少數量。
第2到N+1行:第i+1行的整數表示第i頭奶牛所使用的擠奶機的編號。

樣例數據

樣例輸入 樣例輸出
5
1 10
2 4
3 6
5 8
4 7
4
1
2
3
2
4

 

 

 

 

 

 

<樣例解釋>

下面是擠奶機的時間安排表:(其它合理答案均可)

解析

先按奶牛要求的時間起始點進行從小到大排序,然后維護一個優先隊列,里面以已經開始擠奶的奶牛的結束時間早為優先。然后每次只需要檢查當前是否有奶牛的擠奶工作已經完成的機器即可,若有,則換那台機器進行工作。若沒有,則加一台新的機器。

Code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>

using namespace std;

const int maxn=50000+10;

int order[maxn];

struct Node
{
int st,en,pos;
friend
bool operator<(Node a,Node b)
{
if(a.en==b.en)
return a.st<b.st;
return a.en>b.en;
}
}node[maxn];

bool cmp(Node a,Node b)
{
if(a.st==b.st)
return a.en<b.en;
else
return a.st<b.st;
}

priority_queue
<Node>Q;

int main()
{
int n,ans;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
{
scanf(
"%d%d",&node[i].st,&node[i].en);
node[i].pos
=i;
}
sort(node
+1,node+1+n,cmp);
ans
=1;
Q.push(node[
1]);
order[node[
1].pos]=1;
for(int i=2;i<=n;i++)
{
if(!Q.empty()&&Q.top().en<node[i].st)
{
order[node[i].pos]
=order[Q.top().pos];
Q.pop();
}
else
{
ans
++;
order[node[i].pos]
=ans;
}
Q.push(node[i]);
}
printf(
"%d\n",ans);
for(int i=1;i<=n;i++)
printf(
"%d\n",order[i]);
while(!Q.empty()) Q.pop();
}
return 0;
}

[POJ 3040] 津貼 (Allowance)

題目描述

作為對勤勤懇懇工作的貝茜的獎勵,約翰已經決定開始支付貝茜一個每周津貼。 約翰有N (1≤N≤20)種幣值的硬幣,面值小的硬幣總能整除面值較大的硬幣。(比如說,幣值有如下幾種:1美分,5美分,10美分,50美分…)利用給定的這些硬幣,他將要每周付給貝茜一定金額的津貼C (1 <= C <= 100,000,000)。
請幫他計算出他最多能給貝茜發幾周的津貼。

輸入

第1行:2個用空格隔開的整數N和C。 第2到N+1行:每行兩個整數表示一種幣值的硬幣.第一個整數V (1 <= V <= 100,000,000),表示幣值。 第二個整數B (1 <= B <= 1,000,000),表示約翰擁有的這種硬幣的個數。

輸出

一個整數,表示約翰付給貝茜津貼得最多的周數。

樣例數據

樣例輸入 樣例輸出
3 6
10 1
1 100
5 120
111

 

 

 

 

 

解析

開始看到題目里的整除關系時,整個人是完全懵逼的:這是什么呀?后來想想也就想通了,這個條件實際上就是解題的關鍵。

對於面值大於C的硬幣自然不用說。一枚用一周。
對於面值小於C的硬幣。我們先考慮一個C的方案。要使用的周數最多我們應該就要使錢的利用率最大。也就是說損失的錢最少,盡量不要超過C太多。在不超過C的情況下對於大面值和小面值的優先使用大面值的。因為小面值的組合可得到大面值(整除關系)。留下小面值給后面的組合最優的可能性越大。如這種策略下沒湊夠C的話就找一個最小的面額。使得組合值大於C。(使損失值最小)

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>

const int INF=0x3f3f3f3f;

using namespace std;

struct node
{
int val,mou;
}mon[
25];

int n,c;
int need[25];

bool cmp(node a,node b)
{
return a.val<b.val;
}

int main()
{
int i,ans,ti,rest,lim,mday;
while(~scanf("%d%d",&n,&c))
{
ans
=0;
lim
=-1;
for(i=0;i<n;i++)
scanf(
"%d%d",&mon[i].val,&mon[i].mou);
sort(mon,mon
+n,cmp);
for(i=n-1;i>=0;i--)
if(mon[i].val>=c)
ans
+=mon[i].mou;
else
{
lim
=i;
break;
}
while(1)
{
memset(need,
0,sizeof need);
rest
=c;
for(i=lim;i>=0;i--)
{
if(!mon[i].mou||!rest)
continue;
ti
=rest/mon[i].val;
ti
=min(ti,mon[i].mou);
need[i]
=ti;
rest
-=ti*mon[i].val;
}
if(rest)
{
for(i=0;i<=lim;i++)
{
if(mon[i].val>=rest&&(mon[i].mou-need[i]))
{
need[i]
++;
rest
=0;
break;
}
}
if(rest)
break;
}
mday
=INF;
for(i=0;i<=lim;i++)
if(need[i])
mday
=min(mday,mon[i].mou/need[i]);
ans
+=mday;
for(i=0;i<=lim;i++)
mon[i].mou
-=mday*need[i];
}
printf(
"%d\n",ans);
}
return 0;
}

[POJ 1862] Stripies (Stripies)

題目描述

給出N個物體,分別給出每個的質量,並且兩個物體(假設質量分別為m1,m2)相撞的時候變成一個物體,質量為2*sqrt(m1*m2),並且只會出現兩個兩個物品碰撞的情況。問最終能得到的物體的最小質量是多少。

輸入

第1行包含一個整數N (1 <= N <= 100) 。接下來的N行,每行一個整數,表示一個物體的質量m ( 1<= m <= 10,000 )。

輸出

一個整數,表示最小質量。

樣例數據

樣例輸入 樣例輸出
3
72
30
50
120.000

 

 

 

 

 

解析

首先給出貪心策略:每次選舉兩個當前最大的數值進行運算,直到剩下一個物品。使用優先隊列處理比較方便。

我們可以這樣證明:假設三個物體,質量分別為 a,b,c,(a>=b>=c)。可以證明:2*sqrt(2*c*sqrt(a*b)) <= 2*sqrt(2*a*sqrt(b*c)) 。

Code

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<iostream>
#include<algorithm>


using namespace std;

int main()
{
int n;
while(~scanf("%d",&n))
{
priority_queue
<double> q;
for(int i=0;i<n;++i)
{
double tp;
cin
>>tp;
q.push(tp);
}
while(q.size()>1)
{
double a=q.top();q.pop();
double b=q.top();q.pop();
double tp=2*sqrt(a*b);
q.push(tp);
}
printf(
"%.3f\n",q.top());
}
return 0;
}

[POJ 3262] 保護花朵 (Protecting the Flowers)

題目描述

N (2 ≤ N ≤ 100,000)頭奶牛在Farmer John的花園里吃花,現在為了保護花朵,Farmer John要將這些奶牛都帶回牛棚里去。第i頭牛離牛棚有Ti (1 ≤ Ti ≤ 2,000,000)分鍾的路程, 在等待被送回牛棚的同時,這頭牛還要每分鍾吃掉Di (1 ≤ Di ≤ 100)朵花。而Farmer John每次只能帶一頭奶牛回到牛棚。

現在,要求求出奶牛們在剩下的時間里最少能吃掉多少朵花?

輸入

第1行:包含一個整數N。
第2到N+1行:每一行包含兩個整數Ti和Di,分別表示奶牛回到牛棚需要的時間和每分鍾吃掉的花朵。

輸出

包含一個整數,表示奶牛們最少吃的花朵數。

樣例數據

樣例輸入 樣例輸出
6
3 1
2 5
2 3
3 2
4 1
1 6
86

 

 

 

 

 

 

 

解析

對牛進行排序,排序的標准是,假設牛A與牛B要選一頭趕走,我們首先要選擇破壞最大的一頭牛趕走,留破壞小的牛。他們的破壞着呢麽計算呢?假設先趕走牛A,那么牛B造成的損失是2×TA×DB,先趕走牛B,那么牛A造成的損失是2×TA×DB,所以,只要判斷TA×DB與TA×DB誰大,就知道該先趕走誰了,所以數組排序的標准就是Ti×Dj>Tj×Di

Code

#include<cstdio>
#include
<cstring>
#include
<algorithm>

#define LL long long
#define INF 0x3f3f3f3f

using namespace std;

struct node
{
LL x;
LL y;
}a[
100010];

int cmp(node n,node m)
{
return n.y*m.x>m.y*n.x;
}

int main()
{
int n;
LL sum,num;
while(scanf("%d",&n)!=EOF)
{
sum
=0;
memset(a,
0,sizeof(a));
for(int i=0;i<n;i++)
{
scanf(
"%I64d %I64d",&a[i].x,&a[i].y);
sum
+=a[i].y;
}
sort(a,a
+n,cmp);
num
=0;
for(int i=0;i<n;i++)
{
sum
-=a[i].y;
num
+=sum*(a[i].x*2);
}
printf(
"%I64d\n",num);
}
return 0;
}

 

Time: 2017-07-14

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com