这次训练的题目,两道水题(卡数据类型,有好多人不能1A,我也在内),还有3道题,不是很容易。
一道很水的题,因为我刚开始用了sort,T了一次,后来又忘了是什么原因RE了2次,最后,就直接读入的时候比较的,很水,想多了。
下面简单分析一下吧,由于是要求b/a的最大值,所以只需要把当前的b/a与之前最大的b/a比较就行,如果相同的话(在这了需要主语,如果你用的是交叉相乘来比较,记得用long long,如果你用的是b/a来比较,在判断是否相等的时候记得浮点型的精度问题,可以用abs(max-(double)b/a)<=1e-9 之类的式子来判断,不要直接用等号),再去比较a的大小,由于本身序号就是从小到大的,所以也不用担心这个了(只要a相等的时候别更新答案就行),说的有点多了,附代码吧
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define N 100005
using namespace std;
int main()
{
int t;
double MAX=0;
cin>>t;
while (t--)
{
int n;
int ans,ansa;
MAX=0;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
double c=(double)b/a;
if (MAX<c)
{
MAX=c;
ans=i;
ansa=a;
}
else if(abs(MAX-c)<=1e-10)
{
if (a<ansa)
{
ans=i;
ansa=a;
}
}
}
printf("%d\n",ans);
}
return 0;
}
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">B题,</span><a target=_blank href="http://code.bupt.edu.cn/problem/p/417/" target="_blank" style="font-family: Arial, Helvetica, sans-serif;">boj0417丁神又去谷歌</a>
赤果果的一道0-1背包,记得要用long long 就行,直接附代码
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define N 1005
using namespace std;
long long dp[N];
int a[N],b[N];
int main()
{
int T;
cin>>T;
while (T--)
{
int n,t;
scanf("%d%d",&t,&n);
memset(dp,0,sizeof(dp));
for (int i=1;i<=n;i++)
scanf("%d%d",&a[i],&b[i]);
for (int i=1;i<=n;i++)
for (int j=t;j>=a[i];j--)
dp[j]=max(dp[j],dp[j-a[i]]+b[i]);
printf("%lld\n",dp[t]);
}
return 0;
}
这个题呀,昨天比赛时有思路(PS:今天想了一上午,发现我的思路是在是太难实现了),最终还是选择了lyz大神的思路。
题意大概是求n以内(1--n)的数的全排列中,符合交叉排列的情况(即<><><><><>,大于号小于号交叉出现的情况)总共有多少种,结果对p取余。
我就来说说lyz大神的思路吧,用到了排列组合的思想。
首先,为了方便叙述,我先来给交叉排列的两种情况命一下名,
假设:第一个数大于第二个数的情况叫做D情况,第一个数小于第二个数的情况叫做P情况,对于k个数的排列,我们记D情况共有D[k]种,P情况共有P[k]种,(记 P[0]=P[1]=P[2]=1 那么,就有D[k]=P[k](至于怎么来证明,其实很简单,我们来看两个图
所有D情况都可以抽象成左图图(W型),所有的P情况都可以抽象成右图(M型),
对于D情况,可以抽出n个数中的最大数,分别放到各个凸起点(途中红圈标出),经过一些排列组合的运算,可以得到一个一个结果。
同理,对于P情况,可以抽出n个数中的最小数,分别放到各个凹陷点(图中红圈标出),根据对称的特性,就可以得到跟D情况相同的结果。
所以,我们只需考虑一种情况了,我选取的D情况,经过排列组合的运算,很容易得到如下的关系(sigma代表求和符号
D[k]=sigma(D[i[*D[k-1-i]*C[k-1][k-1-i]) (可能看这个式子看不懂,自己推导一下吧,分别把最大的数放到凸起点就可以了,很容易的)
好了,下面附代码(PS:被memset坑了好久,也学到了一点,在程序中,你如果开了数组但是没有用到,提交时内存是不算的,如果你用了memset(a,0,sizeof(a))来初始化数组,那么这个数组就算是全部被使用了。这题中,如果用memset来初始化组合数的存储数组,就会超出内存限制。算是学到了一点吧!还有就是,如果用动态数组来做的话,是擦边过得,我是第一次交1000ms, T了,第二次993ms过了。最后,注意D数组要用long long,即便不用,运算时也记得转化一下,否则会WA的)。
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 4205
int C[N][N]={0};
long long g[N]={0};
int n,p;
void init()
{
memset(g,0,sizeof(g));
g[3]=2;
g[2]=1;
g[1]=1;
g[0]=1;
for (int i=1;i<=n;i++)
{
C[i][0]=C[i][i]=1;
for (int j=1;j<i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
}
for (int i=4;i<=n;i++)
for (int j=0;j<=i-1;j+=2)
g[i]=(g[i]+(g[j]*g[i-1-j]%p)*C[i-1][i-1-j])%p;
}
int main()
{
while (~scanf("%d%d",&n,&p))
{
init();
printf("%d\n",2*g[n]%p);//输出不用lld是因为取余以后就在int的范围内了。
}
}
D题,boj0427 学姐逗学弟
一道博弈题目,比赛时看错题了。。。今天下午基本上全花在这个题上了,根源还是memset,通过个题我了解到:用memset来初始化数组也是要花O(n)的时间的,它比直接赋值快大约4倍,但是计算复杂度时还是要按照O(n)的复杂度考虑进去的。在此感谢zsp同学帮我找到这个错误。
好了,进入正题。这是一个博弈问题,Every-Sg游戏,直接说思路吧,题目可以自己点开链接来看。
首先,我们定义每个节点的高度为所有人都采取最优策略,一直到游戏结束所需要走的步数。思路很简单,对于必败点,走这一步的人必输,那么他肯定想尽快结束这一个游戏,所以他会选择它的所有子节点中高度最小的来走;对于必胜点,走这一步的人,想要胜利,就必须走到必败点上去,而他又想把游戏时间尽量拉长,所以他肯定会选择所有必败子节点中高度最大的那个节点来走。这样分析的话,就很容易递归的定义出一个点的高度了。然后再根据给的石子的位置来判断,比较高度最大的必胜点和高度最大的必败点的大小,就可以判断是不是学姐赢了。
这个代码是有注释了(为了让zsp同学帮忙找下错误而标注的,再次进行感谢),贴代码
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#define N 100005
using namespace std;
#define INF 10000000
vector <int> tree[N];
bool judgeSg[N]={false};//判断子节点出现的sg值
int father[N]={0};//记录某个节电的父亲节点
int depth[N]={0};//记录“高度”(懒得改了)
int sg[N]={0};//对于单个游戏来说的每个节点的sg值。
int n,m;
void init()//初始化
{
memset(father,0,sizeof(father));
memset(depth,0,sizeof(depth));
memset(sg,0,sizeof(sg));
for (int i=1;i<=n;i++)
tree[i].clear();
}
void solveSg(int now)//now代表当前节点,dfs
{
int MAX=0,MIN=INF;
if (tree[now].size()==0)
{
sg[now]=0;
depth[now]=0;
return ;
}//搜到叶子节点,叶子节点对应的高度为0,sg值为0;
for (int i=0;i<tree[now].size();i++)
{
int son=tree[now][i];
solveSg(son);//dfs
if (MIN>depth[son])
MIN=depth[son];//记录所有子节点中的最小高度(如果当前节点是必败节点的话用得到)
if (sg[son]==0)//子节点为必败态
if (MAX<depth[son])
MAX=depth[son];//记录所有必败子节点中的最大高度(如果当前节点是必胜节点用得到)
}
memset(judgeSg,false,sizeof(tree[now].size()+10));//初始化sg判断数组
for (int i=0;i<tree[now].size();i++)
{
int son=tree[now][i];
judgeSg[sg[son]]=true;//记录出现的sg值
}
int res=0;
while (judgeSg[res]) res++;
if (res==0)//此点为必败态
depth[now]=MIN+1;
else //为必胜态
depth[now]=MAX+1;
sg[now]=res;//当前节点的sg值
}
int main()
{
int T;
cin>>T;
while (T--)
{
int maxWin=0,maxFail=0;//分别记录必胜点和必败点的最大高度
scanf("%d%d",&n,&m);
init();
for (int i=2;i<=n;i++)
{
scanf("%d",&father[i]);
tree[father[i]].push_back(i);
}//动态数组建树(单向的)
solveSg(1);//dfs
for (int i=1;i<=m;i++)
{
int stone;
scanf("%d",&stone);
if (sg[stone]==0)
maxFail=max(maxFail,depth[stone]);
else
maxWin=max(maxWin,depth[stone]);
}
if (maxWin>maxFail)//必胜的游戏时间长,学姐赢
printf("MengMengDa!\n");
else
printf("So sad...\n");
}
return 0;
}
E题,好吧,我承认,还没做,先放放吧!