2014-9-27 NOIP模拟赛

1、栅栏迷宫

田野上搭建了一个黄金大神专用的栅栏围成的迷宫。幸运的是,在迷宫的边界上留出了两段栅栏作为迷宫的出口。更幸运的是,所建造的迷宫是一个“完美的”迷宫:即你能从迷宫中的任意一点找到一条走出迷宫的路。给定迷宫的宽W(1<=W<=38)及长H(1<=H<=100)。 2*H+1行,每行2*W+1的字符以下面给出的格式表示一个迷宫。然后计算从迷宫中最“糟糕”的那一个点走出迷宫所需的步数(就是从最“糟糕”的一点,走出迷宫的最少步数)。(即使从这一点以最优的方式走向最靠近的出口,它仍然需要最多的步数)当然了,黄金大神让你必须只会水平或垂直地在X或Y轴上移动,你不能从来不走对角线。每移动到一个新的方格算作一步(包括移出迷宫的那一步)这是一个W=5,H=3的迷宫:

+-+-+-+-+-+
|         |
+-+ +-+ + +
|     | | |
+ +-+-+ + +
| |     |  
+-+ +-+-+-+

如上图的例子,栅栏的柱子只出现在奇数行或奇数列。每个迷宫只有两个出口。

PROGRAM NAME: maze

INPUT FORMAT:

(file maze.in)

第一行: W和H(用空格隔开) 
第二行至第2*H+1行:  每行2*W+1个字符表示迷宫 

OUTPUT FORMAT:

(file maze.out)

输出一个单独的整数,表示能保证牛从迷宫中任意一点走出迷宫的最小步数。

SAMPLE INPUT

5 3
+-+-+-+-+-+
|         |
+-+ +-+ + +
|     | | |
+ +-+-+ + +
| |     |  
+-+ +-+-+-+

SAMPLE OUTPUT

9

善良的学长:样例输入可以复制进记事本或者文本文档这样看起来更加直观!!!=v=

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 210
using namespace std;
char ch[maxn];
int map[maxn][maxn],n,m,dis[maxn][maxn],ans;
bool vis[maxn][maxn];
int e[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
struct node{
    int x,y,step;
}cur,nxt;
queue<node>q;
void bfs(){
    while(!q.empty()){
        cur=q.front();
        q.pop();
        vis[cur.x][cur.y]=0;
        int x=cur.x,y=cur.y,xx,yy;
        for(int i=0;i<4;i++){
            xx=x+e[i][0];
            yy=y+e[i][1];
            if(xx<=n&&xx>=1&&yy<=m&&yy>1&&!vis[xx][yy]&&!map[xx][yy]){
                nxt.x=xx;nxt.y=yy;nxt.step=cur.step+1;
                if(dis[xx][yy]>nxt.step){
                    dis[xx][yy]=nxt.step;
                    q.push(nxt);
                    vis[xx][yy]=1;
                }
            }
        }
    }
}
int main(){
    //freopen("Cola.txt","r",stdin);
    freopen("maze.in","r",stdin);
    freopen("maze.out","w",stdout);
    memset(dis,127/3,sizeof(dis));
    scanf("%d%d",&m,&n);
    m=m*2+1;n=n*2+1;
    getchar();
    for(int i=1;i<=n;i++){//第i行 
        gets(ch+1);
        for(int j=1;j<=m;j++){
            if(ch[j]!=' ')map[i][j]=1;
            if(ch[j]==' '){
                if(i==1||i==n||j==1||j==m){
                    cur.x=i;cur.y=j;cur.step=0;
                    dis[cur.x][cur.y]=0;
                    q.push(cur);
                    vis[cur.x][cur.y]=1;
                }
            }
        }
    }
    bfs();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(!map[i][j])
            ans=max(ans,dis[i][j]);
        }
    }
    printf("%d",(ans+1)/2);
    return 0;
}
100分 bfs

2、人偶师(alice.cpp/c/pas)

【题目描述】

n点m双向边的图,每个点有2个状态:开和关。每次操作改变一个点的状态,以及与其有边直接相连的点的状态。问开启所有点至少需要多少次操作。

【输入格式】

第一行2个整数n,m。

第二行n个整数,第i个数表示第i点的状态,0为关,1为开。

第3..m+2行,每行2个整数a,b,表示a和b直接相连,同一条边不会出现多次。

【输出格式】

第一行一个整数k表示最少的操作次数,所有数据保证至少有一组可行解。

第二行k个整数,表示操作的点的编号。

【样例输入】

4 3

1 1 0 0

2 3

1 3

2 4

【样例输出】

3

1 2 3

【数据范围】

对于30%的数据,1<=n<=10,0<=m<=40

对于60%的数据,1<=n<=30,0<=m<=100

对于100%的数据,1<=n<=40,0<=m<=500

善良的学长:虽然解可能不只一个,但是你只要输出任意一个即可得分0.0

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#define pa pair<int,int>
#define inf 1000000000
#define ll long long
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,mn=inf;
int a[45];
ll ed,p[45],bin[45];
bool used[45],ans[45];
void dfs(int x,int now,int k){
    if(k>=mn)return;
    if(x==n+1){
        if(now==ed){
            mn=k;
            for(int i=1;i<=n;i++)
                ans[i]=used[i];
        }
        return;
    }
    dfs(x+1,now,k);
    used[x]=1;
    dfs(x+1,now^p[x],k+1);
       used[x]=0;
}
int main(){
    freopen("alice.in","r",stdin);
    freopen("alice.out","w",stdout);
    bin[1]=1;
    for(int i=2;i<45;i++)bin[i]=bin[i-1]<<1;
    n=read();m=read();ed=1<<n;ed--;
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=m;i++){
        int a=read(),b=read();
        p[a]+=bin[b];p[b]+=bin[a];
    }
    for(int i=1;i<=n;i++)p[i]+=bin[i];
    int now=0;
    for(int i=1;i<=n;i++)
        if(a[i])now+=bin[i];
    dfs(1,now,0);
    printf("%d
",mn);
    for(int i=1;i<=n;i++)
        if(ans[i])printf("%d ",i);
    return 0;
}
60分 暴力

3、交通(traffic.c/cpp/pas)

黄金大神国的首都位于hzwer河中的一座岛屿。一道上班的时候,成千上万辆汽车通过岛屿从西岸的住宅区(由桥连接岛的西部)到东岸的工业区(由桥连接岛的东部)。
    该岛类似于矩形,它的边平行于主方向。故可将它看作是笛卡尔坐标系中的一个A*B的矩形,它的对角分别为(0, 0)和(A, B)。
    岛上有n个交通节点(后宫建筑),编号为1…n,第i个节点坐标为(xi, yi)。如果一个节点的坐标为(0, y),它就位于岛的西岸。类似的,坐标为(A, y)的节点位于岛的东岸。各个节点由街道连接,每条街道用线段连接两个节点。街道有单向行驶或双向行驶之分。除端点外任意两条街道都没有公共点。也没有桥梁或者隧道。
    你不能对道路网络形状做任何其他假设。沿河岸的街道或节点可能没有入口或者出口街道。由于交通堵塞日趋严重,黄金大神想快速治理好他的国家,于是聘请你测试岛上当前的道路网是否足够。要求你写一个程序确定从岛的西岸的每个节点能够到达东岸的多少个节点。

【输入格式】

第1行包含4个整数n, m, A, B,分别表示hzwer市中心的节点数,街道数和岛屿的尺寸。
    接下来的n行,每行包含两个整数xi,yi (0≤xi≤A,0≤yi≤B),表示第i个节点的坐标。任意两个节点的坐标都不相同。
再往下的m行表示街道,每条街道用3个整数ci, di, ki(1≤ci, di≤n, ci≠di, ki∈{1,2}),表示节点ci、di有街道连接,如果ki=1,表示从ci到di的街道是单向的,否则,这条街道可以双向行驶。每个无序对{ci, di}最多出现1次。
    你可以假设西岸节点中至少有1个能够到达东岸的一些节点。

【输出格式】

为每个西岸节点输出1行,表示这个节点出发能够到达东岸的节点数目

【样例输入】

12 13 7 9

0 1

0 3

2 2

5 2

7 1

7 4

7 6

7 7

3 5

0 5

0 9

3 9

1 3 2

3 2 1

3 4 1

4 5 1

5 6 1

9 3 1

9 4 1

9 7 1

9 12 2

10 9 1

11 12 1

12 8 1

12 10 1

【样例输出】

4

4

0

2

【数据范围】

对于30%的数据,n, m≤6000

对于100%的数据,1≤n≤300000, 0≤m≤900000,1≤A,B≤10^9

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 300010
int n,m,A,B,s[maxn],t[maxn],ss[maxn],c1,num,head[maxn],f[maxn],ans;
bool vis[maxn];
struct node{
    int to,pre;
}e[900010];
struct Node{
    int x,y;
}point[maxn];
void Insert(int from,int to){
    e[++num].to=to;
    e[num].pre=head[from];
    head[from]=num;
}
void dfs(int now){
    if(vis[now])return;
    vis[now]=1;
    for(int i=head[now];i;i=e[i].pre){
        int to=e[i].to;
        if(vis[to]==1)continue;
        if(t[to])ans++;
        dfs(to);
    }
}
int cmp(int x,int y){
    return point[x].y<point[y].y;
}
int main(){
    //freopen("Cola.txt","r",stdin);
    freopen("traffic.in","r",stdin);
    freopen("traffic.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&A,&B);
    int x,y,z;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&x,&y);
        if(x==0){s[++c1]=i;ss[i]=1;point[i].x=x,point[i].y=y;}
        if(x==A){t[i]=1;}
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        if(z==1)Insert(x,y);
        if(z==2){
            Insert(x,y);
            Insert(y,x);
        }
    }
    sort(s+1,s+c1+1,cmp);
    for(int i=c1;i>=1;i--){
        ans=0;
        memset(vis,0,sizeof(vis));
        dfs(s[i]);
        printf("%d
",ans);
    }
}
50分 对每个起点暴力dfs

原文地址:https://www.cnblogs.com/thmyl/p/7421212.html