您的位置:首页 > 其它

20170909考试总结

2017-09-11 20:50 204 查看

第一题:回文数组 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
;
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

;
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]
=ans[1][n-1]+res;
int d=ans[1]
-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]
=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
;
int st
,ed
,fir
,tar[M],nxt[M];
bool dp
[1<<K];
vector<int> q
;
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。。。。还是算基本正常的发挥。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: