電子科技大學第九屆ACM趣味程序設計競賽(熱身賽)題解

比賽地址: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 ;
        }
}
相關文章
相關標籤/搜索