20170909考試總結

第一題:迴文數組 palindrome

題目描述:如果數組A中每個元素i,都有A[i]=A[N-i+1],(第一個元素下標爲1).則稱數組A是迴文數組。現在給出一個數組,你可以這樣修改數組中的元素:將相鄰的兩個元素替換爲這兩個元素之和。注意,這樣操作一次之後,數組中的元素個數減1.請問最少需要多少次操作,才能得到一個迴文數組。

題解:從兩邊向中間比較兩端的數必須相等。如果不相等,則小的那一端必須合併相鄰兩個數。
成績:AC
分析:大水題(๑´ω`๑)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1000000+10;
inline void getint(int&num){
    char c;num=0;
    while((c=getchar())<'0'||c>'9');
    while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
}
int n,arr[N];
int main(){
    //freopen("palindrome.in","r",stdin);
    //freopen("palindrome.out","w",stdout);
    getint(n);
    for(int i=1;i<=n;i++)
        getint(arr[i]);
    int l=0,r=n+1,ans=0;
    while(l<r){
        int a=arr[++l],b=arr[--r];
        if(l>=r) break ;
        for(;a!=b;ans++){
            if(l>=r) break ;
            if(a<b) a+=arr[++l];
            else b+=arr[--r];
        }
    }
    printf("%d\n",ans);
}

第二題:填充表格 table

題目描述:構造一個n*n的矩陣滿足:
1.每一行的平均值在這一行出現過。
2.每一列的平均值在這一列出現過。
3.表格中每個數都不一樣。

題解:控制每一行的平均數爲倒數第二個數,那麼每一行如此構造:
a-(n-2)*d, a-(n-3)*d …a-2d,a-d,a,a+(1+2+…+n-2)*d
每一行d取一,每一列d取n*(n-1)/2。
成績:AC
分析:剛開始還是卡了一下°(°ˊДˋ°) °,想了很多莫名其妙的思路,但最後幸好還是懵出來辣~(≧▽≦)/~。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100+10;
int n,ans[N][N];
int main(){
    //freopen("table.in","r",stdin);
    //freopen("table.out","w",stdout);
    scanf("%d",&n);
    if(n==2){
        printf("-1\n");return 0;
    }
    if(n&1){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                ans[i][j]=(i-1)*n+j;
    }
    else{
        for(int i=1;i<n;i++) ans[1][i]=i;
        int res=(1+n-2)*(n-2)/2;
        ans[1][n]=ans[1][n-1]+res;
        int d=ans[1][n]-ans[1][1]+1;
        for(int i=2;i<=n;i++){
            if(i==n) ans[i][1]=ans[i-1][1]+res*d; 
            else ans[i][1]=ans[i-1][1]+d;
            for(int j=2;j<n;j++)
                ans[i][j]=ans[i][j-1]+1;
            ans[i][n]=ans[i][n-1]+res;
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            printf("%d%c",ans[i][j],j==n?10:32);
}

第三題:遊戲 game

題目描述:丹尼爾和他朋友斯蒂芬玩一個遊戲。他們畫了一棵樹,樹有n個節點。再節點1處放了一個硬幣。丹尼爾矇住眼睛,然後他們開始玩遊戲:
1.首先丹尼爾選擇一個節點,將它標記。
2.斯蒂芬將硬幣移到一個相鄰的且沒有標記過的節點,然後將硬幣的上一個位置打好標記。
他們一直重複這兩個步驟,直到斯蒂芬無法再移動硬幣,則遊戲結束。因爲丹尼爾是蒙着眼睛的,所以他任意時刻都不知道硬幣的位置。當然,他還是知道樹的結構和硬幣開始再哪個節點。現在,請問丹尼爾是否能在k個回合之內結束遊戲。即斯蒂芬只能移動硬幣少於k次。

題解:首先可以刪掉深度大於K(斯蒂芬只要到達深度爲k的節點,他便贏了;所以深度大於k的節點根本不需要訪問)或小於k的節點(斯蒂芬不可能訪問這棵子樹,否則他必輸無疑)。那麼現在樹的形狀就變成了每個葉子節點的深度均爲k,只要斯蒂芬能走到一個葉子節點,他就贏了,否則就輸了。對於丹尼爾,他最佳的標記方法爲每一深度選一個標記(刪除它與它子樹,最後使刪完所以葉子節點)
有一個結論:K^2>=n時丹尼爾一點獲勝:
f(i)表示節點i及以下最早的分叉深度。
d表示刪除了d次後,還存在一個節點f(i)=d的最小的d(前d次所刪的節點到根都沒有分叉,也就是除根外沒有公告祖先)所以這d次每次至少刪除K+K-d個節點。
設還剩下n2個節點:n2=n-d*(K+K-d);
設還要刪K2次:K2=K-d;
可以推出當K2*K2>=n2時,K*K>=n
又因爲前d次已封住了其他子樹,所以n2,K2時能獲勝,n,K時也能獲勝。
所以K^2>=n時丹尼爾一定獲勝。
dp[i][j]表示能否在j狀態刪除前i個葉子節點,j是一個2進制數,表示某一層是否被刪除了節點。
成績:12/60
分析:寫了個亂搞貪心,本來有60,數組開小了變成了12… (๑>m<๑) ,貪心的思路基本是錯的,因爲當時想着想着就想成了丹尼爾知道硬幣在哪裏的思路(๑•́ ₃ •̀๑)


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=400+5;
const int M=800+5;
const int K=20;
int n,k,cnt,u,v,leaf=1,dep[N];
int st[N],ed[N],fir[N],tar[M],nxt[M];
bool dp[N][1<<K];
vector<int> q[N];
void link(int a,int b){
    tar[++cnt]=b;
    nxt[cnt]=fir[a],fir[a]=cnt;
}
void dfs(int x,int fa){
    if(dep[x]==k-1){
        st[x]=leaf++,ed[x]=leaf;
        return ;
    }
    st[x]=leaf;
    for(int i=fir[x];i;i=nxt[i])
        if(tar[i]!=fa){
            dep[tar[i]]=dep[x]+1;
            dfs(tar[i],x);
        }
    ed[x]=leaf;
}
bool Dp(){
    dp[1][0]=1;
    for(int i=2;i<=n;i++)
        q[st[i]].push_back(i);
    for(int i=1;i<leaf;i++)
        for(int j=0;j<(1<<k);j++){
            if(!dp[i][j]) continue ;
            int siz=q[i].size();
            for(int u=0;u<siz;u++)
                if(!(j>>dep[q[i][u]]&1))
                    dp[ed[q[i][u]]][j|(1<<dep[q[i][u]])]=1;
        }
    for(int i=0;i<(1<<k);i++)
        if(dp[leaf][i]) return 1;
    return 0;
}
int main(){
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    scanf("%d %d",&n,&k);
    if(k*k>=n)
        printf("DA\n"),exit(0);
    for(int i=1;i<n;i++){
        scanf("%d %d",&u,&v);
        link(u,v),link(v,u);
    }
    dep[1]=-1,dfs(1,0);
    printf("%s\n",Dp()?"DA":"NE");
}

總結:這次一二題都AC了 (⁄ ⁄•⁄ω⁄•⁄ ⁄),然而第三題還是有不該出現的失誤(๑→‿ฺ←๑)。但失誤還不是很嚴重qwq。。。。還是算基本正常的發揮。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章