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。。。。还是算基本正常的发挥。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值