比賽地址:http://acm.uestc.edu.cn/#/contest/show/191算法
A題 小羽塗色優化
題意:spa
在x軸的正半軸上,問你是否存在一段區間[L,R]其中包含r個奇數和g個偶數。code
分析:blog
對於區間的起點與終點,咱們能夠分4種狀況進行討論。three
1.起點爲奇數,終點爲奇數,這種狀況下r=g+1.string
2.起點爲奇數,終點爲偶數,這種狀況下r=g.io
3.起點爲偶數,終點爲奇數,這種狀況下r=g.class
4.起點爲偶數,終點爲偶數,這種狀況下r+1=g.test
綜上所述,只要知足|r - g| ≤ 1 就能夠符合條件。
可是,這裏有一個坑點,那就是區間不能爲空,因此說r=g=0的時候答案應該是「NO」.
標程:
#include<stdio.h> int main(){ int r , g ; scanf( "%d%d" , &r , &g ) ; if ( r == 0 && g == 0 ) printf( "NO\n" ) ; else if ( r - g > 1 || g - r > 1 ) printf( "NO\n" ) ; else printf( "YES\n" ) ; return 0 ; }
B題 座位分配
題意:
有N對異性情侶,P名單身男性,Q名單身女性須要到食堂就餐,你須要給他們安排四人桌。其中,情侶有一個限制條件,就是必須在同一張桌上就餐,且本身對面和旁邊的座位不能出現其餘異性(斜對角的位置沒有限制),問最少須要多少張四人桌才能知足全部人的就餐要求。
分析:
假設沒有限制條件,一共有M人須要就餐,問須要多少張四人桌,這個問題就很簡單,答案就是M/4+(M % 4 ? 1 : 0 ),也能夠寫成(M + 3) /4.
可是情侶有特別的限制條件,咱們就得想辦法把這種條件轉換或者消除。
首先咱們來考慮一對情侶該怎麼坐,一共有三種作法:面對面坐,同時坐桌子的一邊,坐桌子的兩個角。
首先,若是情侶坐桌子兩角的話,剩下兩個位置不管是安排男性仍是女性都沒法符合題意,只能空着,很浪費空間。
而情侶面對面坐或者坐桌子的一邊其實是等價的,把桌子旋轉90度就能從一種狀況轉移到另外一種狀況,且不會改變安排方式的合法性。
因此咱們就把這兩種狀況統一當作面對面坐,而面對面坐的話,男生旁邊能夠坐男生,女生旁邊能夠坐女生,空間還有利用的餘地。
因此,給一對情侶安排座位確定面對面坐最優。
如今考慮多對情侶安排位置的問題。顯然,你能夠把兩對情侶安排在同一桌,男生坐一邊,女生坐一邊,這樣的話他們兩對情侶佔滿了一張桌子,這樣安排確定最優。
因此說若是有偶數對情侶,他們剛好能佔據N/2張桌子,剩下的人再按照無限制條件的方式計算。
若是有奇數對情侶,先把N-1對情侶安排佔據(N-1)/2張桌子,而後剩下那對情侶面對面坐,旁邊還有兩個空位,
若是有單身男性還能夠安排一位單身男性坐情侶那桌,若是有單身女性還能夠安排一位單身女性坐情侶那桌,剩下的人再按照無限制條件的方式計算。
總的思路很簡單,優先給情侶安排好位置,再考慮單身狗的位置,這個問題就迎刃而解了~
標程:
#include<stdio.h> int main(){ int N , P , Q , ans ; scanf( "%d%d%d" , &N , &P , &Q ) ; if ( N % 2 == 0 ) ans = ( 2 * N + P + Q + 3 ) / 4 ; else{ int d = P + Q ; if ( P == 0 ^ Q == 0){ d-- ; ans = ( 2 * N + 1 + 3 ) / 4 ; ans += ( d + 3 ) / 4 ; } else ans = ( 2 * N + P + Q + 3 ) / 4 ; } printf( "%d\n" , ans ) ; return 0 ; }
C題 馬里奧餅店
題意:
你須要到馬里奧餅店買N個價值爲K元的麪包。9折卡可使你的消費優惠10%,可是有一個四捨五入的原則,精確到元。
你能夠屢次購買你所須要的N個麪包,使總花費最小,問這個最小的總花費是多少?
分析:
首先,由於有四捨五入的原則,咱們就應該儘量地使總價格打完9折後可以舍,不要入;也就是說打完折後小數點是0.1,0.2,0.3,0.4都是咱們所但願的。
因此,原來的總價格個位數是6,7,8,9是比較好的。所以,咱們能夠採用貪心的思想,不斷積累麪包,當面包的價錢個位數6,7,8,9結尾時咱們就把它買下,
即可以實現舍的目標,也就能夠轉換爲第一次使得麪包價錢個位數爲6,7,8,9結尾時的麪包數量爲m,每買m個麪包就能多便宜1塊錢。所以就證實了貪心思想的正確性。
注意在計算打9折算四捨五入價格時建議採用整數運算,能夠先乘9再除10,對餘數判斷大於等於5就加1,用浮點型運算可能會有精度問題。
標程:
#include<stdio.h> int calc( int x ){ int temp = x * 9 ; int ans = temp / 10 ; int mod = temp % 10 ; if ( mod >= 5 ) ans++ ; return ans ; } int main(){ int N , K ; scanf( "%d%d" , &N , &K ) ; int ans = 0 ; int res = 0 ; int i ; for( i = 1 ; i <= N ; i++ ){ res += K ; if ( res % 10 > 5 ){ ans += calc( res ) ; res = 0 ; } } ans += calc( res ) ; printf( "%d\n" , ans ) ; return 0 ; }
D題 FIFA Online3
題意:
模擬題,給你11名球員的基礎能力值,每名球員可能有5種能力加成,問你每名球員的綜合能力值是多少。
分析:
模擬題沒有什麼特別的套路,關鍵就是要仔細讀題,注意細節。
由於每種加成相互獨立,因此你能夠按照順序一種一種進行加成。
注意隊套加成裏俱樂部加成和國家加成不能疊加的問題,就把兩種加成分別算出來,再取一個大的做爲他的加成。
特別注意,0卡的球員沒有隊套加成,可是他能夠做爲同一俱樂部或國家隊隊員給其餘隊員貢獻隊套加成。
這道題的讀入有技巧,具體請看標程。
另外還有一個小坑,那就是俱樂部名稱和國家隊名稱可能相同。
標程:
#include<stdio.h> #include<string.h> char name[15][25] , team[15][25] , nat[15][25] ; int val[15] , jing[15] , qiang[15] ; int two , three , five , eight ; int q[11] = { 0 , 5 , 6 , 7 , 8 , 10 , 12 , 14 , 17 , 20 , 24 } ; int main(){ int x , y , z ; scanf( "%d-%d-%d" , &x , &y , &z ) ; int i ; two = three = five = eight = 0 ; for( i = 1 ; i <= 11 ; i++ ){ scanf( "%s %d Lv.%d %d %s %s" , name[i] , &val[i] , &jing[i] , &qiang[i] , team[i] , nat[i] ) ; if ( qiang[i] >= 2 ) two++ ; if ( qiang[i] >= 3 ) three++ ; if ( qiang[i] >= 5 ) five++ ; if ( qiang[i] >= 8 ) eight++ ; } int a , b , c , d ; scanf( "%d%d%d%d" , &a , &b , &c , &d ) ; for( i = 1 ; i <= 11 ; i++ ){ val[i] += jing[i] - 1 ; val[i] += q[qiang[i]] ; if ( qiang[i] >= 2 && two >= 6 ) val[i]++ ; if ( qiang[i] >= 3 && three >= 6 ) val[i]++ ; if ( qiang[i] >= 5 && five >= 6 ) val[i]++ ; if ( qiang[i] >= 8 && eight >= 6 ) val[i]++ ; int j ; int t , n ; t = n = 0 ; for( j = 1 ; j <= 11 ; j++ ){ if ( strcmp( team[i] , team[j] ) == 0 ) t++ ; if ( strcmp( nat[i] , nat[j] ) == 0 ) n++ ; } t = t >= n ? t : n ; if ( t >= 6 && qiang[i] ) val[i] += 2 ; if ( t >= 9 && qiang[i] ) val[i]++ ; if ( i == 1 ) val[i] += a ; if ( i >= 2 && i <= 2 + x - 1 ) val[i] += b ; if ( i >= 2 + x && i <= 2 + x + y - 1 ) val[i] += c ; if ( i >= 2 + x + y ) val[i] += d ; printf( "%s %d\n" , name[i] , val[i] ) ; } return 0 ; }
E題 秦隊長猜測
題意:
給你一個大於等於6且不超過10^9的整數N,請你把它拆成三個質數的和。
分析:
咱們知道,判一個數是否爲質數的最基本方法是O(sqrt(N))的。
你能夠垂手可得地相出一種算法複雜度爲O((Nsqrt(N))^3)的作法,可是這道題確定不能這麼作。
咱們發現一個特別奇妙的性質,那就是2和3都是質數。
若是N是偶數,你就選其中一個質數爲2;若是N是奇數,你就選其中一個質數爲3。這樣剩下兩個質數的和均爲偶數了。
而後,題目就轉換成了任何一個大於等於4的偶數都能寫成兩個質數的和。咦,這不是哥德巴赫猜測嗎?
要怎麼作呢,O((Nsqrt(N))^2)?彷佛仍是不行的喲。
其實,你能夠優化到O(Nsqrt(N)),可是這樣看上去複雜度仍是爆炸。
可是,你要膽子大一點嘛,由於它的複雜度不是嚴格的O(Nsqrt(N)),而是你找到了答案就能往外跳。
通過試驗發現,你能夠很快的就找到答案,也就是說就算一個偶數N大到1e9,基本上均可以在2e2以內找到一個數p,使得p和N-p均爲質數。
這是爲何呢?其實這能夠用生日悖論來解釋。
1e9內大約有5e7個質數,也就是質數的機率大約爲5%,合數的機率大約爲95%。
你能夠從小到大枚舉質數2,3,5,7...那麼N-2爲合數,N-3爲合數,N-5爲合數,N-7爲合數同時成立你纔會接着往下找,否則的話你就找到了一組合法的解,能夠直接跳出。
所以,進行K次操做後,你依然找不到解的機率約爲(0.95)^K;當K=200時,機率僅爲0.004%,因此說你能夠很快的找出一組解而後輸出跳出就能夠了。
標程:
#include<stdio.h> #include<math.h> #define bool int #define false 0 #define true 1 bool prime( int N ){ if ( N <= 1 ) return false ; int i ; for( i = 2 ; i <= sqrt( N ) ; i++ ) if( N % i == 0 ) return false ; return true ; } int main(){ int N ; scanf( "%d" , &N ) ; if ( N % 2 == 0 ){ printf( "2 " ) ; N -= 2 ; } else{ printf( "3 " ) ; N -= 3 ; } int i ; for( i = 2 ; ; i++ ) if ( prime( i ) && prime( N - i ) ){ printf( "%d %d\n" , i , N - i ) ; return 0 ; } }