2020 年百度之星·程式設計大賽 - 初賽一
Drink
Accepts: 1896 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Description我們有nn種不同的飲料,每種飲料有無限多瓶,第ii種飲料一瓶提供x[i]x[i]毫升的水分,包含y[i]y[i]卡路里。
現在我們需要選擇一種飲料一直喝,直到補充了至少mm毫升的水分,我們想使得攝入的卡路里總和最小。請求出這個最小值。
一旦開啟一瓶飲料,就一定要喝完。
Input第一行一個整數test(1 \le test \le 100)test(1≤test≤100)表示資料組數。
對於每組資料,第一行兩個整數n, m(1 \le n \le 100, 1 \le m \le 10000)n,m(1≤n≤100,1≤m≤10000)。
接下來nn行,每行兩個整數x[i], y[i](1 \le x[i], y[i] \le 100)x[i],y[i](1≤x[i],y[i]≤100)。
Output對於每組資料,一行一個整數表示答案。
Sample Input2 1 10 3 3 2 10 3 3 2 1Sample Output
12 5
解題思路:因為n種飲料種每種飲料的補充水分x和和包含的卡路里都不同,所以從頭到尾遍歷看哪種飲料消耗的卡路里最少(注意這裡是 一種飲料一直喝)
我們便可以的到如下程式碼:
#include<cstdio> #include<iostream> #define INF 0x3f3f3f3f using namespace std; int n,m,t; int x[105],y[105]; int main(void) { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(int i=0;i<n;++i) { scanf("%d%d",&x[i],&y[i]); }int sum=INF;//初始化總卡路里 for(int i=0;i<n;++i) { if(m%x[i])//如果不能被整除 sum=min(sum,m/x[i]*y[i]+y[i]); else//能被整除 sum=min(sum,m/x[i]*y[i]); } printf("%d\n",sum); } return 0; }
開始沒看到一種飲料一直喝,還以為是完全揹包問題(不會寫),結果後來發現是個大水題 T_T
GPA
Accepts: 1554 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Description小沃沃一共參加了 4 門考試,每門考試滿分 100 分,最低 0 分,分數是整數。
給定四門考試的總分,請問在最優情況下,四門課績點的和最高是多少?
分數與績點之間的對應關係如下:
95~100 4.3
90~94 4.0
85~89 3.7
80~84 3.3
75~79 3.0
70~74 2.7
67~69 2.3
65~66 2.0
62~64 1.7
60~61 1.0
0~59 0
Input第一行一個正整數test(1 \le test \le 401)test(1≤test≤401)表示資料組數。 接下來testtest行,每行一個正整數xx表示四門考試的總分(0 \le x \le 400)(0≤x≤400)。
Output對於每組資料,一行一個數表示答案。答案保留一位小數。
Sample Input2 0 400Sample Output
0.0 17.2
解題思路:仔細分析其實我們不需要哪個分數區間,我們只需要區間的最小的值代表那個績點,也就是說 95~100 -> 4.3 在最優的情況下,其實我們不需要在意在區間取值,95就能表示績點4.3
總共有四個成績,十一個分數段,那麼就有兩種迴圈方式,第一個是對分數段迴圈,第二個就是對四個成績迴圈。
解法1:
#include<cstdio> #include<iostream> using namespace std; int t,x,key; int main(void) { scanf("%d",&t); while(t--) { scanf("%d",&x); double sum=0,sum2; for(int a=0;a<5;++a) { if(a*95>x) break; for(int b=0;b<5;++b) { if(a*95+b*90>x) break; for(int c=0;c<5;++c) { if(a*95+b*90+c*85>x) break; for(int d=0;d<5;++d) { if(a*95+b*90+c*85+d*80>x) break; for(int e=0;e<5;++e) { if(a*95+b*90+c*85+d*80+e*75>x) break; for(int f=0;f<5;++f) { if(a*95+b*90+c*85+d*80+e*75+f*70>x) break; for(int g=0;g<5;++g) { if(a*95+b*90+c*85+d*80+e*75+f*70+g*67>x) break; for(int h=0;h<5;++h) { if(a*95+b*90+c*85+d*80+e*75+f*70+g*67+h*65>x) break; for(int i=0;i<5;++i) { if(a*95+b*90+c*85+d*80+e*75+f*70+g*67+h*65+i*62>x) break; for(int j=0;j<5;++j) { key=a*95+b*90+c*85+d*80+e*75+f*70+g*67+h*65+i*62+j*60; sum2=a*4.3+b*4.0+c*3.7+d*3.3+e*3.0+f*2.7+g*2.3+h*2.0+i*1.7+j*1.0; if(key<=x) { sum=max(sum,sum2); } else break; } } } } } } } } } } printf("%.1lf\n",sum); } return 0; }
解法2:
#include<cstdio> #include<iostream> using namespace std; int a[11]={95,90,85,80,75,70,67,65,62,60,0}; double b[11]={4.3,4.0,3.7,3.3,3.0,2.7,2.3,2.0,1.7,1.0,0}; int main(void) { int x,t; scanf("%d",&t); while(t--) { scanf("%d",&x); double sum=0; for(int i=0;i<11;++i) { for(int j=0;j<11;++j) { for(int k=0;k<11;++k) { for(int l=0;l<11;++l) { int s=a[i]+a[j]+a[k]+a[l]; if(s<=x) { sum=max(sum,b[i]+b[j]+b[k]+b[l]); } } } } } printf("%.1lf\n",sum); } return 0; }
Dec
Accepts: 1284 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Description初始有a, ba,b兩個正整數,每次可以從中選一個大於 1 的數減 1,最後兩個都會減到 1,我們想知道在過程中兩個數互質的次數最多是多少。
Input第一行一個正整數test(1 \le test \le 1000000)test(1≤test≤1000000)表示資料組數。
接下來 test 行,每行兩個正整數a, b(1 \le a, b \le 1000)a,b(1≤a,b≤1000)。
Output對於每組資料,一行一個整數表示答案。
Sample Input1 2 3Sample Output
4 樣例解釋 2 3 -> 1 3 -> 1 2 -> 1 1
解題思路:這道題開始可能會有人覺得這是數學題目,其實不然,再通過仔細閱讀題目之後,我們能發現一個問題資料有1e6組而且a和b剛好小於等於1e3,這不就明顯擺著動態規劃嘛
由於每次從a,b兩個數中選一個然後減一,那麼我們就能把這個問題拆成小問題,a和b的互質的數就去找 a-1和b互質的數,a和b-1互質的數,然後選出最大的值賦給dp[a][b],但是要注意a和b本身就互質的情況
由於1和其他數字都互質所以我們可以初始化dp[i][1]=dp[1][i]=i。
狀態轉移方程式為:
dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j])
&&if(gcd(i+1,j+1)==1)
dp[i+1][j+1]++;
我們可以得到如下程式碼:
#include<cstdio> #include<algorithm> using namespace std; #define maxn 1002 int t,a,b; int dp[maxn][maxn]; int main(void) { for(int i=1;i<maxn;++i) dp[i][1]=dp[1][i]=i; for(int i=1;i<maxn;++i) { for(int j=1;j<maxn;++j) { if(__gcd(i+1,j+1)==1) { dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j])+1; } else { dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]); } } } scanf("%d",&t); while(t--) { scanf("%d%d",&a,&b); printf("%d\n",dp[a][b]); } return 0; }
這裡有個比較玄學的問題,就是maxn的大小,如果maxn大於等於1005就會T(我習慣開1005),換句話說maxn的值只能取1001,1002,1003,1004(親身試過的T^T)
這是1005的那一發,當時還以為這不是動歸的題,,,
然後最後時間快截止了改了下maxn的大小改成了1002