第一题:回文数组 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。。。。还是算基本正常的发挥。