9.28noip模拟试题

 

 

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

 

Bfs(妈的把判重放到出队的时候就炸了....)

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 310
using namespace std;
int n,m,head=1,tail,f[maxn][maxn];
char s[maxn][maxn],c;
struct node{
    int x,y,t;
}q[maxn*maxn];
int xx[4]={0,0,1,-1};
int yy[4]={1,-1,0,0};
int Bfs(){
    int mx=0;
    while(head<=tail){
        node p=q[head++];
        int x=p.x,y=p.y,t=p.t;
        mx=max(mx,t);
        for(int i=0;i<4;i++){
            int nx=x+xx[i];
            int ny=y+yy[i];
            if(nx>0&&nx<=n&&ny>0&&ny<=m&&f[nx][ny]==0&&s[nx][ny]==' ')
                f[nx][ny]=1,q[++tail]=(node){nx,ny,t+1};
        }
    }
    return mx;
}
int main()
{
    freopen("maze.in","r",stdin);
    freopen("maze.out","w",stdout);
    scanf("%d%d",&m,&n);c=getchar();
    m=m*2+1;n=n*2+1;
    for(int i=1;i<=n;i++)gets(s[i]+1);
    for(int i=1;i<=m;i++)
        if(s[1][i]==' ')q[++tail]=(node){1,i,0},f[1][i]=1;
    for(int i=1;i<=m;i++)
        if(s[n][i]==' ')q[++tail]=(node){n,i,0},f[n][i]=1;
    for(int i=1;i<=n;i++)
        if(s[i][1]==' ')q[++tail]=(node){i,1,0},f[i][1]=1;
    for(int i=1;i<=n;i++)
        if(s[i][m]==' ')q[++tail]=(node){i,m,0},f[i][m]=1;
    printf("%d\n",Bfs()+1>>1);
    return 0;
}
View Code

 

 

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

 暴力+剪枝70

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,m,num,head[50],c[50],s[50],r[50],f[50],sum=100,cr[50];
struct node{
    int v,pre;
}e[1010];
void Add(int from,int to){
    num++;e[num].v=to;
    e[num].pre=head[from];
    head[from]=num;
}
void Dfs(int now){
    int S=0;
    for(int i=1;i<=n;i++)
        if(f[i])S++;
    if(S>=sum)return;
    if(now==n+1){
        int flag=0;
        for(int i=1;i<=n;i++)
            if(!r[i]){
                flag=1;break;
            }
        if(flag==0){
            int cnt=0;
            for(int i=1;i<=n;i++)
                if(f[i])cnt++;
            if(cnt<sum){
                sum=cnt;cr[0]=0;
                for(int i=1;i<=n;i++)
                    if(f[i])cr[++cr[0]]=i;
            }
        }
        return;
    }
    for(int i=1;i<=n;i++)
        if(c[i]==s[i]&&r[i]==0)return;
    c[now]++;
    for(int i=head[now];i;i=e[i].pre)
        c[e[i].v]++;
    Dfs(now+1);
    for(int i=head[now];i;i=e[i].pre)
        c[e[i].v]--;
    r[now]=!r[now];f[now]=1;
    for(int i=head[now];i;i=e[i].pre)
        c[e[i].v]++,r[e[i].v]=!r[e[i].v];
    Dfs(now+1);
    for(int i=head[now];i;i=e[i].pre)
        c[e[i].v]--,r[e[i].v]=!r[e[i].v];
    c[now]--;r[now]=!r[now];f[now]=0;
}
int main()
{
    freopen("alice.in","r",stdin);
    freopen("alice.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&r[i]);s[i]++;
    }    
    int u,v;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        Add(u,v);Add(v,u);
        s[u]++;s[v]++;
    }
    Dfs(1);
    printf("%d\n",sum);
    for(int i=1;i<=cr[0];i++)
        printf("%d ",cr[i]);
    return 0;
}
View Code

正解分两段暴力+hash

/*
map会T两个点...
改成hash 似乎还不对 但cena测试过了....
分两块Dfs 前半段记录信息A
代表改成某个状态是的按下灯的方案
这里用longlong存储的
后半段结束的时候 now^end代表的是对应前半段的状态
这时候在用刚才存下的查一下 取小 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#define ll long long
#define mod 2333333
using namespace std;
ll n,m,a[50],p[50],Mi[50],end,N,falg,mx=1000,ans;
ll num,head[mod+10];
struct node{
    ll v1,v2,pre;
}e[mod*20];
//map<ll,ll>A;//每个灯这个状态时 按的状态 
ll Cal(ll x)
{
    ll sum=0;
    for(int i=1;i<=n;i++)
        if(Mi[i]&x)sum++;
    return sum;
}
void Insert(ll now,ll used){
    ll ha=(now+9+now*19*31)%mod;
    num++;e[num].v1=now;e[num].v2=used;
    e[num].pre=head[ha];head[ha]=num;
}
ll Find(ll now){
    ll ha=(now+9+now*19*31)%mod;
    for(int i=head[ha];i;i=e[i].pre)
        if(e[i].v1==now)return e[i].v2;
    return 0;
}
void Dfs(ll x,ll now,ll used){//第几个开关  每个灯亮不亮的状态  每个灯按不按的状态 
    if(x==N+1){
        if(now==end){
            if(Cal(used)<mx){
                mx=Cal(used);ans=now;
            }
        }
        if(!falg){
            ll t=Find(now);
            //A[now];
            if(!t)Insert(now,used);
            //A[now]=used;
            else if(Cal(t)>Cal(used))Insert(now,used);
            //A[now]=used;
        }
        else{
            ll t=Find(now^end);
            //A[now^end];//从now按到目标状态的差 按出来 每个灯按不按的状态 
            if(!t)return;
            ll sum=Cal(t)+Cal(used);
            if(sum<mx){
                mx=sum;ans=t+used;
            }    
        }
        return ;
    }
    Dfs(x+1,now,used);
    Dfs(x+1,now^p[x],used+Mi[x]);//按下了x  
}
int main()
{
    //freopen("alice.in","r",stdin);
    //freopen("alice.out","w",stdout);
    scanf("%d%d",&n,&m);
    Mi[1]=1;
    for(int i=2;i<=45;i++)
        Mi[i]=Mi[i-1]<<1;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(!a[i])end+=Mi[i];
    }
    int u,v;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        p[u]|=Mi[v];p[v]|=Mi[u];//按下这个灯 有那几个会变 
    }
    for(int i=1;i<=n;i++)
        p[i]|=Mi[i];
    N=n/2;Dfs(1,0,0);
    falg=1;N=n;Dfs(n/2+1,0,0);
    printf("%d\n",mx);
    for(int i=1;i<=n;i++)
        if(Mi[i]&ans)printf("%d ",i);
    return 0;
}
View Code

 

 

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行,每行包含两个整数xiyi (0≤xi≤A,0≤yi≤B),表示第i个节点的坐标。任意两个节点的坐标都不相同。
再往下的m行表示街道,每条街道用3个整数ci, di, ki1≤ci, di≤n, ci≠di, ki∈{1,2}),表示节点cidi有街道连接,如果ki=1,表示从cidi的街道是单向的,否则,这条街道可以双向行驶。每个无序对{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

暴力dfs

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 300010
using namespace std;
int n,m,A,B,num,head[maxn],f[maxn],p[maxn],tot,sum;
struct P{
    int o,Y,cnt;
}Xi[maxn];
struct node{
    int v,pre;
}e[maxn*3*2];
int init(){
    int x=0;char s=getchar();
    while(s<'0'||s>'9')s=getchar();
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x;
}
int cmp(const P &a,const P &b){
    return a.Y>b.Y;
}
void Add(int from,int to){
    num++;e[num].v=to;
    e[num].pre=head[from];
    head[from]=num;
}
void Dfs(int now){
    if(p[now]==1)sum++;
    for(int i=head[now];i;i=e[i].pre){
        int v=e[i].v;
        if(f[v])continue;
        f[v]=1;Dfs(v);
    }
}
int main()
{
    freopen("traffic.in","r",stdin);
    freopen("traffic.out","w",stdout);
    n=init();m=init();A=init();B=init();
    int x,y,z;
    for(int i=1;i<=n;i++){
        x=init();y=init();
        if(x==0)Xi[++tot]=(P){i,y,0};
        else if(x==A)p[i]=1;
    }
    for(int i=1;i<=m;i++){
        x=init();y=init();z=init();
        Add(x,y);if(z==2)Add(y,x);
    }
    for(int i=1;i<=tot;i++){
        memset(f,0,sizeof(f));
        sum=0;f[Xi[i].o]=1;
        Dfs(Xi[i].o);
        Xi[i].cnt=sum;
    }
    sort(Xi+1,Xi+1+tot,cmp);
    for(int i=1;i<=tot;i++)
        printf("%d\n",Xi[i].cnt);
    return 0;
}
View Code

 

posted @ 2016-09-28 22:29  一入OI深似海  阅读(510)  评论(0编辑  收藏  举报