NOIP2014 DAY1
目录
前言
NOIP2018即将来临,教练为了训练我们这些初中的人,就把这套题发给我们做。。。
然而,第二题我毫无思路,第三题却少打了一句话就爆炸了QAQ。。。
绝望。。。。。。。。。。。。
T1 生活大爆炸版石头剪刀布
题目
思路
很明显的一道模拟题,唯一要注意的是:我们要把给定的表格的下半部分(灰框区域)自己填好。
(看不懂的自己去看代码吧)
正解代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int Maxn=200;
int f[][6]={{0,-1,1,1,-1},
{-2,0,-1,1,-1},
{-2,-2,0,-1,1},
{-2,-2,-2,0,1},
{-2,-2,-2,-2,0}};
//1表示小A胜,0表示平,-1表示小B胜,-2表示表格中灰框部分
int N,Na,Nb;
int a[Maxn+5],b[Maxn+5];
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d %d %d",&N,&Na,&Nb);
for(int i=1;i<=Na;i++)
scanf("%d",&a[i]);
for(int j=1;j<=Nb;j++)
scanf("%d",&b[j]);
for(int i=0;i<5;i++)
for(int j=0;j<i;j++)
f[i][j]=-f[j][i];//注意填充灰框部分!!!
int cnta,cntb,i,j;
i=j=1,cnta=cntb=0;
for(int k=1;k<=N;k++) {
if(i>Na)i=1;
if(j>Nb)j=1;//注意出拳有周期!
int now=f[a[i]][b[j]];
if(now==-1)cntb++;
if(now==1)cnta++;
i++,j++;
}//模拟
printf("%d %d\n",cnta,cntb);
return 0;
}
T2 联合权值
题目
题外话
我写了一个树形DP,结果发现我无法写出状态转移方程。。。
然后,我想到了暴力做法,结果发现要超时。。。
然后,我就GG了。。。QAQ。。。
考试结束后我去看了一下大佬的博客,发现这道题竟然如此简单。。。
思路
首先题目告诉你这是一棵树。
题目要求是一个有序点对 。我们不难发现这两个的值是相同的,所以我们就只计算 ,最后将答案乘 即可。
接下来想到了什么?树形DP??LCA??还是什么乱七八糟的东西???
这就是一个普通的DFS。。。(我怎么就没有想到呢???)
首先考虑暴力做法:
对于树上的每个节点 ,首先枚举它的每条边 ,接着枚举它的儿子 的边 ,再将其乘上,累加到答案中去。
明显超时。。。。。。
我们考虑一下优化:
当我们枚举以 为中间点的出边时,设这些连着的点为 ,其中 为 的邻接点的数量。
不难得到联合权值: 。
用分配律合并一下: 。
因此,在枚举 的出边 时,设 为已经枚举过的点的权值和,则我们就可以省去枚举 的时间,时间复杂度则降至
正解代码
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Maxn=200000;
const int Mod=10007;
vector<int> G[Maxn+2];
inline void addedge(int u,int v) {
G[u].push_back(v);
}
int N,W[Maxn+2];
int maxx=-1,ans=0;
void DFS(int u,int fa) {
int sum=W[fa],maxt=W[fa];
for(int i=0;i<G[u].size();i++) {
int v=G[u][i];
if(v==fa)continue;
DFS(v,u);
ans=(ans+sum*W[v])%Mod;
sum=(sum+W[v])%Mod;
maxx=max(maxx,maxt*W[v]);
maxt=max(maxt,W[v]);
}
}
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d",&N);
for(int i=1;i<N;i++) {
int u,v;
scanf("%d %d",&u,&v);
addedge(u,v);
addedge(v,u);
}
for(int i=1;i<=N;i++)
scanf("%d",&W[i]);
DFS(1,0);
ans=(ans*2)%Mod;//注意取模!
printf("%d %d\n",maxx,ans);
return 0;
}
T3 飞扬的小鸟
题目
题外话
这道题坑掉了我两个小时。。。
因为我用了一个小时来调。。。
就是因为这句话:
当我发现这一切的时候,考试已经结束了QAQ。。。
思路
不难看出,这道题向上跳跃是一个完全背包,向下掉是一个01背包。
所以我们定义状态 为当前在第 列(换句话说,也就是第 单位时间)第 行所需要的点击次数。
记 为上面的水管所达到的高度, 为下面水管所达到的高度。
向下的状态转移方程非常好写:
其中,
且
(我就是少写了这句话。。。)
再来考虑向上的:
我们不难列出状态转移方程:
其中, 且 。
注意在计算结束后将不合法状态值全部赋为INF(即有水管的地方)
但是我们不难看出,这是一个要超时的算法,所以我们必须对它进行优化:
对于状态 ,我们可以发现,这个状态可以由两个状态转移而来:
,即上一单位时间点击一次,然后到达 ;
,即这个单位时间再点一次,然后到达 。
所以,这种转移的时间复杂度就优化为了 。
注意,由于在玩的时候是可以顶到天花板的,所以我们还需要一次转移,即再点一次顶到天花板上。
综上所述,我们不难列出状态转移方程:
注意一定要将不合法状态赋为INF!!!
正解代码
注意由于C++中没有负下标,所以我将 数组平移了一格,其它不变。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Maxn=10000;
const int Maxm=1000;
const int INF=0x3f3f3f3f;
int N,M,K;
int X[Maxn+1],Y[Maxn+1];
int d[Maxn+1],u[Maxn+1];
int f[Maxn+1][Maxm+1];
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d %d %d",&N,&M,&K);
for(int i=1;i<=N;i++)
d[i]=0,u[i]=M+1;
//预处理,将没有水管的位置的u[i]设为M+1
for(int i=0;i<N;i++)
scanf("%d %d",&X[i],&Y[i]);
for(int i=1;i<=K;i++) {
int p,l,h;
scanf("%d %d %d",&p,&l,&h);
d[p]=l,u[p]=h;
}
for(int i=1;i<=N;i++)
for(int j=0;j<=M;j++)
f[i][j]=INF;
f[0][0]=INF;
for(int i=1;i<=N;i++) {
for(int j=X[i-1];j<=M;j++) {//点了一下
f[i][j]=min(f[i][j],f[i-1][j-X[i-1]]+1);
f[i][j]=min(f[i][j],f[i][j-X[i-1]]+1);
}
for(int j=M-X[i-1];j<=M;j++) {//到顶
f[i][M]=min(f[i][M],f[i-1][j]+1);
f[i][M]=min(f[i][M],f[i][j]+1);
}
for(int j=d[i]+1;j<u[i];j++)//不点
if(j+Y[i-1]<=M)
f[i][j]=min(f[i][j],f[i-1][j+Y[i-1]]);
for(int j=1;j<=d[i];j++)f[i][j]=INF;
for(int j=u[i];j<=M;j++)f[i][j]=INF;
}
int cnt=K;//记录越过多少根水管
int ans=INF;//记录点击的最少次数
for(int i=N;i>=1;i--) {
for(int j=d[i]+1;j<u[i];j++)
ans=min(ans,f[i][j]);
if(ans<INF)break;//注意找到答案即输出!
if(u[i]!=M+1)cnt--;
//当未到达终点且有一根水管没有过时计数器应减1
}//找答案
if(cnt==K)printf("1\n%d\n",ans);
else printf("0\n%d\n",cnt);
#ifdef DEBUG
for(int i=1;i<=N;i++) {
for(int j=1;j<=M;j++)
if(f[i][j]==INF)printf("INF ");
else printf("%3d ",f[i][j]);
puts("");
}
#endif
return 0;
}