1. 程式人生 > >2016廣東工業大學新生杯決賽解題報告

2016廣東工業大學新生杯決賽解題報告

a.pigofzhou的巧克力棒

舉一些例子,可以得出把長度為n的棒劃分最多高興值的方法是:設最大的不超過n的2的整數冪是k,則分為2^k和n-2^k兩份。

2^k則是每次分為兩半,而剩下的再遞迴以同樣的方法劃分。

f(n)=f(2^k)+f(n-2^k),f(2^k)=2*f(2^(k-1))+1.

最開始時自己錯誤地認為每次減半就好了,結果n=6時先分為了兩個長度3,不夠優,正確的方法是分為4和2兩份。

#include<cstdio>
using namespace std;
 
 
int t,n;
 
int f(int t)
{ 
    if(t==1)return 0;
    for(int i=30;;i--)
    {
        if(t==(1<<i))
        {
            return 2*f(t/2)+1;
        }
        else if(t>(1<<i))
        {
            return f(1<<i)+f(t-(1<<i));
        }
    }
}
 
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        printf("%d\n",f(n));
    }
    return 0;
}

後來看大佬的blog,瞭解到第一題可以和第二題用同樣的程式碼ac,理解了一下,大概是自底向上的思路:假如n=9,則1~9中2存在2這個因子的有2,4,6,8四個數,記錄下4個,然後每個數提出去一個因子2,結果是1,2,3,4,再找有2這個因子的數木,為2,相當於把這四個兩兩合併,再2/2,為1,共4+2+1=7種,就是這樣自底向上,逐層合併。

#include<iostream>
using namespace std;
  
int main()
{
    int t;
    long long n,ans;
    cin>>t;
    while(t--)
    {
        cin>>n;
        ans=0;
          
        while(n)
        {
            ans+=n/2;
            n/=2;
        }
          
        cout<<ans<<endl;
    }
    return 0;
}

 

b.Zhazhahe究竟有多二

統計n!中含有素數因子k的個數,數學推導如下:

設n!=1*2*3*......*n=(1*k)*(2*k)*(3*k)*......*(m*k)*a           //其中m=n/k,a為不含因子k的數的乘積

=(k^m)*(m!)*a,接著以同樣的方法遞迴求m!.

#include<iostream>
using namespace std;
 
int main()
{
    int t;
    long long n,ans;
    cin>>t;
    while(t--)
    {
        cin>>n;
        ans=0;
         
        while(n)
        {
            ans+=n/2;
            n/=2;
        }
         
        cout<<ans<<endl;
    }
    return 0;
}

c.剁手女生節

這道題要注意:不能想當然地認為買3本的價錢比買2本貴,買2本比買一本貴,必須詳盡地考慮清楚所有的情況。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
 
long long t,n,a,b,c;
 
int main()
{
    scanf("%lld",&t);
    while(t--)
    {
        scanf("%lld%lld%lld%lld",&n,&a,&b,&c);
        if(n%4==0)printf("0\n");
        else if(n%4==3)printf("%lld\n",min(min(a,b+c),3*c));
        else if(n%4==2)printf("%lld\n",min(min(2*a,b),2*c));
        else printf("%lld\n",min(min(3*a,a+b),c));
    }
    return 0;
 } 

d.勤奮的漣漪2

這題其實也不是很難,但是自己在第21行丟了d[i]=0,使得多組資料受到上一組殘留資料的影響,結果死活找不出來這個bug,第二天才靈機一動在高數課上用手機ac了這道題。

#include<iostream>
using namespace std;
   
int t,n,a[105],ans,d[105];
   
void solve()
{
    for(int i=1;i<=n;i++)
    {
        if(a[i]==3)
        {
            if(d[i-1])d[i]=3-d[i-1];
            else
            {
                int j;
                for(j=i+1;j<=n;j++)if(a[j]==1||a[j]==2)break;
                if(j<=n)d[i]=((j-i)%2==0) ? a[j] : 3-a[j];
                else d[i]=1;
            }
        }
        else if(a[i]==0){ans++;d[i]=0;}
        else if(a[i]==1)
        {
            if(d[i-1]==1)
            {
                ans++;
                d[i]=0;
            }
            else d[i]=1;
        }
        else if(a[i]==2)
        {
            if(d[i-1]==2)
            {
                ans++;
                d[i]=0;
            }
            else d[i]=2;
        }
    }
}
   
int main()
{   
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i];
        ans=0;
        solve();
        cout<<ans*-24<<endl;
    }
    return 0;
}

e.窮遊中國在統題

一個區間的元素可以形成一組的條件是這個區間以後的所有元素都大於或等於這個區間的所有元素,即區間後的最小值>=區間的最大值。於是從後往前預處理一下每個元素後面的所有元素的最小值,再從前往後掃一遍,維護當前最大值,就可以了。

#include<iostream>
#include<algorithm>
using namespace std;
 
int main()
{
    int t,n,a[100005];
    int minn[100005],maxn,ans;
     
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i];
         
        ans=0;
        maxn=-1;
        minn[n]=a[n];
        for(int i=n-1;i>0;i--)minn[i]=min(a[i],minn[i+1]);
        for(int i=1;i<n;i++)
        {
            maxn=max(maxn,a[i]);
            if(maxn<=minn[i+1])ans++;
        }
        cout<<ans+1<<endl;
        if(t)cout<<endl;
    }
    return 0;
}

看大佬的blog有一個思路是把原陣列sort()一下,對於每一個元素,它的原始位置和排序後的位置及其之間的所有元素必須在同一個組裡,按照這個思路,我寫了程式,結果runtime error,好像是因為sort()崩潰,自己也不瞭解sort()的內部實現,把陣列換成結構體之後sort()就可以了,sort()崩潰的問題佔坑以後解決。

下面是ac程式碼:

#include<cstdio>
#include<algorithm>
using namespace std;
  
int t,n;
 
struct node{
    int v,pre;
}a[100005];
  
bool cmp(node i,node j)
{
    return i.v<j.v || (i.v==j.v && i.pre<j.pre);
}
  
int main()
{  
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i].v);
        for(int i=1;i<=n;i++)a[i].pre=i;
        sort(a+1,a+1+n,cmp); 
        int pos=1,r=1,ans=0; 
        while(pos<=n)
        {
            while(pos<=r)
            {
                r=max(a[pos++].pre,r);
            }
            ans++;
            r=a[pos].pre;
        }
        printf("%d\n",ans);
        if(t)printf("\n");
    }
    return 0;
} 

下面是自己的re程式碼:

#include<cstdio>
#include<algorithm>
using namespace std;
  
int t,n,a[100005],pre[100005];
  
bool cmp(int i,int j)
{     
    return a[i]<=a[j];
}
  
int main()
{  
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)pre[i]=i;
        sort(pre+1,pre+1+n,cmp); 
        int pos=1,r=1,ans=0; 
        while(pos<=n)
        {
            while(pos<=r)
            {
                r=max(pre[pos++],r);
            }
            ans++;
            r=pre[pos];
        }
        printf("%d\n",ans);
        if(t)printf("\n");
    }
    return 0;
} 

f.神偷TMK

不說了。

g.神偷TMK後續

組合數。用楊輝三角或等式c(n,k)=c(n,k-1)*(n-k+1)/k都可以。紫書有詳解。

h.《為什麼會變成這樣呢》

先排序,排好後相等的元素會相鄰,然後就好找了。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
 
int t,n,a[1000005];
int b[2],num;
 
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        num=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        sort(a+1,a+1+n);
        for(int i=2;i<n;i++)if(a[i]!=a[i-1] && a[i]!=a[i+1])b[num++]=a[i];
        if(a[1]!=a[2])b[num]=a[1];
        else if(a[n-1]!=a[n])b[num]=a[n];
        printf("%d %d\n",min(b[1],b[0]),max(b[1],b[0]));
    }
    return 0;
 } 

i.只會做水題的jiakin

先記錄下任意兩種水管上下或者左右是否可以連線,然後從右下角搜尋到左上角就可以了。

 

#include<cstdio>
#include<iostream>
using namespace std;
 
int t,n,m,a[1005][1005][2];
bool have[6][6][5];
 
void init()
{
     have[2][0][3]=have[2][0][4]=1;
     have[2][1][1]=have[2][1][2]=1;
     have[2][2][2]=have[2][2][3]=1;
     have[2][3][1]=have[2][3][4]=1;
     have[2][4][1]=have[2][4][3]=1;
     have[2][5][2]=have[2][5][4]=1;
     have[3][0][2]=have[3][0][3]=have[3][0][4]=1;
     for(int i=1;i<4;i++)have[3][1][i]=1;
     for(int i=1;i<=4;i++)if(i!=2)have[3][2][i]=1;
     for(int i=1;i<=4;i++)if(i!=3)have[3][2][i]=1;
     for(int i=1;i<=4;i++)have[3][2][i]=1;
}
 
bool ok(int x,int y)
{
    return x>=1&&x<=n&&y>=1&&y<=m;
}
 
bool f(int x,int y)
{
    if(x==1&&y==1)return 1;
    if(have[a[x][y][0]][a[x][y][1]][1]&&ok(x-1,y)&&have[a[x-1][y][0]][a[x-1][y][1]][2])if(f(x-1,y))return 1;
    if(have[a[x][y][0]][a[x][y][1]][3]&&ok(x,y-1)&&have[a[x][y-1][0]][a[x][y-1][1]][4])if(f(x,y-1))return 1;
    return 0;
}
 
int main()
{
    init();
    cin>>t;
    for(int num=1;num<=t;num++)
    {
        cin>>n>>m;
        getchar();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)scanf("(%d,%d)",&a[i][j][0],&a[i][j][1]);
            getchar();  
        }
        printf("Case %d\n",num);
        printf(f(n,m)?"Well done!\n":"What a pity!\n");
    }
    return 0;
}

j.質方數

先篩素數,篩到sqrt(n)即可,然後暴力從小到大列舉素數,對其平方,記錄與n最近的質方數和對應的最小距離。這居然都能過!

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
 
int t,n,v[20005],minn,ans;
 
int main()
{
    for(int i=2;i<20000;i++)
      if(!v[i])for(int j=i*i;j<20000;j+=i)v[j]=1;
     
    scanf("%d",&t);
    while(t--)
    {
        minn=(1<<30);
        scanf("%d",&n);
        for(int i=2;i<20000;i++)if(!v[i])
        { 
            if(abs(i*i-n)<minn)
            {
                minn=abs(i*i-n);
                ans=i*i;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
 }