NOIP2014 DAY1 模拟赛赛后总结

T1 生活大爆炸版石头剪刀布

  大水题,不解释,打表强过

代码

#include<iostream>
#include<cstdio>
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
#define M 233
using namespace std;
int win[10][10];//1±íʾµÚÒ»¸öÈËÓ®£¬2±íʾµÚ¶þ¸öÈËÓ®£¬0±íʾƽ¾Ö 
//0¼ôµ¶£¬1ʯͷ£¬2²¼£¬3òáòæÈË£¬4˹²¨¿Ë 
int n,na,nb;
int A[M],B[M];
int main(){
    win[0][0]=0,win[0][1]=2,win[0][2]=1,win[0][3]=1,win[0][4]=2;
    win[1][0]=1,win[1][1]=0,win[1][2]=2,win[1][3]=1,win[1][4]=2;
    win[2][0]=2,win[2][1]=1,win[2][2]=0,win[2][3]=2,win[2][4]=1;
    win[3][0]=2,win[3][1]=2,win[3][2]=1,win[3][3]=0,win[3][4]=1;
    win[4][0]=1,win[4][1]=1,win[4][2]=2,win[4][3]=2,win[4][4]=0;
    cin>>n>>na>>nb;
    int cnta=0,cntb=0;
    FOR(i,1,na)scanf("%d",&A[i]);
    FOR(i,1,nb)scanf("%d",&B[i]);
    FOR(i,1,n){
        int x=i%na;
        int y=i%nb;
        if(x==0)x=na;
        if(y==0)y=nb;
        if(win[A[x]][B[y]]==1)cnta++;
        else if(win[A[x]][B[y]]==2)cntb++;
    }
    cout<<cnta<<" "<<cntb<<endl;
    return 0;
}

T2 联合权值

题目描述

  无向连通图 G 有 n 个点,n-1 条边。点从 1 到 n 依次编号,编号为 i 的点的权值为 Wi , 每条边的长度均为 1。图上两点(u, v)的距离定义为 u 点到 v 点的最短距离。对于图 G 上的点
  对(u, v),若它们的距离为 2,则它们之间会产生Wu×Wv 的联合权值。
请问图 G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?

输入

输入文件名为 link.in。 第一行包含 1 个整数 n。
  接下来 n-1 行,每行包含 2 个用空格隔开的正整数 u、v,表示编号为 u 和编号为 v 的点 之间有边相连。
  最后 1 行,包含 n 个正整数,每两个正整数之间用一个空格隔开,其中第 i 个整数表示 图 G 上编号为 i 的点的权值为 Wi。

输出

  输出共 1 行,包含 2 个整数,之间用一个空格隔开,依次为图 G 上联合权值的最大值 和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对 10007 取余。

数据说明

对于 30%的数据,1 < n ≤ 100; 对于 60%的数据,1 < n ≤ 2000;
对于 100%的数据,1 < n ≤ 200,000,0 < Wi ≤ 10,000。

题解

  这道题看似只有两层,但如果是一个菊花图,那就没有办法了。
其实只要枚举一个点作为中心点,记录它的根节点,那么与这个中心点相连的所有点都可以满足了。因此秩序要维护一下前面几个点的前缀和,每次先与父节点相乘,再乘上前缀和即可。

代码

#include<cstdio>
#include<iostream>
#include<vector>
#define M 200050
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
const int P=10007;
vector<int>edge[M];
int w[M];
int n;
long long ans,sum;
struct Water{
    void DFS(int x,int f){
        long long cnt=0;
        int max1=0,max2=0;
        FOR(i,0,edge[x].size()-1){
            int y=edge[x][i];
            if(y==f)continue;
            DFS(y,x);
            sum+=1LL*w[y]*w[f];
            sum%=P;
            sum+=1LL*w[y]*cnt;
            sum%=P;
            cnt+=w[y];
            ans=max(ans,1LL*w[y]*w[f]);
            if(w[y]>max1)max2=max1,max1=w[y];
            else if(w[y]>max2)max2=w[y];
            ans=max(ans,1LL*max1*max2);
        }
    }
    void solve(){
        FOR(i,1,n-1) {
            int a,b;
            scanf("%d%d",&a,&b);
            edge[a].push_back(b);
            edge[b].push_back(a);
        }
        FOR(i,1,n)scanf("%d",&w[i]);
        DFS(1,0);
        cout<<ans<<" "<<sum*2%P<<endl;
    }
}perfect;
int main(){
    cin>>n;
    perfect.solve();
    return 0;
}

小结

  对于每一道题,如果有一些特殊的情况,如此题只有两层,要找到特殊情况的特殊性质,然后再去思考

T3 飞翔的小鸟

题目描述

  Flappy Bird 是一款风靡一时的休闲手机游戏。玩家 需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让 小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了 水管或者掉在地上的话,便宣告失败。
为了简化问题,我们对游戏规则进行了简化和改编:
1. 游戏界面是一个长为 n,高为 m 的二维平面,其中有
k 个管道(忽略管道的宽度)。
2. 小鸟始终在游戏界面内移动。小鸟从游戏界面最左边 任意整数高度位置出发,到达游戏界面最右边时,游 戏完成。
3. 小鸟每个单位时间沿横坐标方向右移的距离为 1,竖直移动的距离由玩家控制。如 果点击屏幕,小鸟就会上升一定高度 X,每个单位时间可以点击多次,效果叠加; 如果不点击屏幕,小鸟就会下降一定高度 Y。小鸟位于横坐标方向不同位置时,上 升的高度 X 和下降的高度 Y 可能互不相同。
4. 小鸟高度等于 0 或者小鸟碰到管道时,游戏失败。小鸟高度为 m 时,无法再上升。

  现在,请你判断是否可以完成游戏。如果可以,输出最少点击屏幕数;否则,输出小鸟 最多可以通过多少个管道缝隙。

FLAPPY BIRD

输入

  第 1 行有 3 个整数 n,m,k,分别表示游戏界面的长度,高度和水管的数量,每两个 整数之间用一个空格隔开;
  接下来的 n 行,每行 2 个用一个空格隔开的整数 X 和 Y,依次表示在横坐标位置 0~n-1 上玩家点击屏幕后,小鸟在下一位置上升的高度 X,以及在这个位置上玩家不点击屏幕时, 小鸟在下一位置下降的高度 Y。
接下来 k 行,每行 3 个整数 P,L,H,每两个整数之间用一个空格隔开。每行表示一 个管道,其中 P 表示管道的横坐标,L 表示此管道缝隙的下边沿高度为 L,H 表示管道缝隙 上边沿的高度(输入数据保证 P 各不相同,但不保证按照大小顺序给出)。

输出

  第一行,包含一个整数,如果可以成功完成游戏,则输出 1,否则输出 0。 第二行,包含一个整数,如果第一行为 1,则输出成功完成游戏需要最少点击屏幕数,
否则,输出小鸟最多可以通过多少个管道缝隙。

题解

  这道题可以用动态规划来做,几乎是一道裸的动归,但是直接动归会超时,到底复杂度堆在哪里呢?
  我们可以这么认为,由于可以点多次,所以一定会有一些重复的,每次只需要维护前一个坐标与下面一个坐标的DP值即可

代码

/*
written by ShimaKZ
*/
#include<iostream>
#include<cstdio>
#include<queue>
#define M 10050
#define FOR(i,a,b) for(register int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(register int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
const int INFINITY=1e9;
struct click {
    int up,down;
} A[M];
int pic[M][1050];
int n,m,k;
int tube[M];
int ans=INFINITY;
struct PERFECT {
    int dp[M][1050];
    void solve() {
        FOR(i,1,n)FOR(j,1,m)dp[i][j]=INFINITY;
        FOR(i,1,n) {
            FOR(j,A[i-1].up+1,m) {
                if(dp[i-1][j-A[i-1].up]+1<dp[i][j])
                    dp[i][j]=dp[i-1][j-A[i-1].up]+1;
                if(dp[i][j-A[i-1].up]+1<dp[i][j])
                    dp[i][j]=dp[i][j-A[i-1].up]+1;
                if(j==m) {
                    FOR(l,m-A[i-1].up+1,m) {
                        if(dp[i-1][l]+1<dp[i][j])
                            dp[i][j]=dp[i-1][l]+1;
                        if(dp[i][l]+1<dp[i][j])
                            dp[i][j]=dp[i][l]+1;
                    }
                }
            }
            FOR(j,1,m-A[i-1].down)
            if(dp[i][j]>dp[i-1][j+A[i-1].down])dp[i][j]=dp[i-1][j+A[i-1].down];
            FOR(j,1,m)if(pic[i][j])dp[i][j]=INFINITY;
        }
        FOR(j,1,m)if(dp[n][j]!=INFINITY&&dp[n][j]<ans)ans=dp[n][j];
        if(ans!=INFINITY) {
            puts("1");
            cout<<ans<<endl;
        } else {
            puts("0");
            int mx=0;
            DOR(i,n,0) {
                bool flag=false;
                FOR(j,1,m) {
                    if(dp[i][j]!=INFINITY) {
                        mx=i;
                        flag=true;
                        break;
                    }
                }
                if(flag)break;
            }
            int check=0;
            FOR(i,1,k)if(tube[i]<=mx)check++;
            cout<<check<<endl;
        }
    }
} perfect;
int main() {
    cin>>n>>m>>k;
    FOR(i,0,n-1)scanf("%d%d",&A[i].up,&A[i].down);
    FOR(i,1,k) {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        tube[i]=a;
        FOR(j,0,b)pic[a][j]=1;
        FOR(j,c,m)pic[a][j]=1;
    }
    perfect.solve();
    return 0;
}

总结

这次考得其实也不是很理想,最后一题的BFS没有写对,反而DP水了70分,结果切分了只有30分,今后的比赛,如果暴力很麻烦并且其他的方法很显然,并且很简单,优先写其他的方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值