USACO-Section1.3Wormholes[其他]

2017-05-25

题目

输入N表示有N个虫洞,然后N组数据表示虫洞的坐标。有一只奶牛可以也仅可以向x轴正方向移动,所以当虫洞满足一定条件时,它从某个点出发就可能陷入无限循环中。你需要考虑到虫洞的每一种两两配对情况,并且计算出在多少种情况下这只奶牛可能陷入无限循环。

样例

4
0 0
1 0
0 1
1 1
此时有两种情况(0,0)-(1,0),(0,1)-(1,1)是一种 ,(0,0)-(1,1),(0,1)-(1,0)是第二种,而还有一种(0,0)-(0,1),(1,0)-(1,1)则不会陷入循环中。

题解

>   第一个问题是配对,如何保证不重不漏地将虫洞两两配对,我采用了递归的方法。首先定义一个结构体数组,每个结构体表示一个门,里面有一个num变量用于匹配,初始为0,匹配时令其等于另一个num==0的门的序号,并同时将另外那个门的num等于该门的序号,然后进行下一次匹配。
    门的序号并非输入时的顺序,而是用了sort函数对这个结构体数组按先行后列的顺序排序。排序方法是自己写一个排序方法的函数作为sort函数的参数,这样可以直接由结构体内的x,y变量进行整个结构体的排序,详情见代码。同时这样的排序也会在下面模拟奶牛行进时起到作用。
    匹配结束后(设置一个参数表示已匹配的个数),开始模拟奶牛行进的过程。我将进门出门放在一个函数里,用一个参数表示所进/出的门,另一个表示进还是出,进门则搜寻某门的Num值等于此门的序号,出门则进入下一个序号的门(如果y变量相同的话),这里显现出了排序的另一个作用,判断y变量相同直接进入下一个门即可。
    下一个问题是如何判断循环。我想的是如果进入一个已经进过的门,或是由一个已经出过的门再次走出,则可以判定是进入了循环,所以在进出门操作前判断此门是否进出过即可。由每个门进入开始,模拟完毕返回是否循环。

代码

/*
ID: xcwhkh1
LANG: C++
TASK: wormhole
*/
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
struct node
{
    int x,y;
    int num;//匹配用 
    int in;
    int out;
}door [14];
bool st(node a,node b)//用于sort 
{
    if(a.y<b.y)//先排纵坐标,行相同则排横坐标 
        return true;
    if(a.y==b.y)
        if(a.x<b.x)
            return true;
    return false;
}
int n,sum=0;
int  go(int i,int io)//传送、行进都算作一次go,io表示进出门 
{
    if(io==0)//出
    {
        if(door[i].out==1)
            return 1;
        door[i].out=1;//如果已经从该门出去过则循环 
        for(int j=i+1;j<=n;j++)
            if(door[j].y==door[i].y)
                return (go(j,1));//找到该行下一个门进入 
        return 0;//此门后无门,出循环 
    }
    else//进 
    {
        if(door[i].in==1)
            return 1;
        door[i].in=1;//如果进入过该门则循环 
        for(int j=1;j<=n;j++)
            if(i==door[j].num)
                return (go(j,0));//找配对的门 
    }
    return 0;
}
void npair(int temp)//递归匹配虫洞 
{
    if(temp==n)
    {   
        for(int i=1;i<=n;i++)
        {
            for(int f=1;f<=n;f++)
            {
                door[f].in=0;
                door[f].out=0;
            }
            if(go(i,1))//go即为走的过程,返回1表示循环 
            {
                sum++;
                break;
            }
        }
        return;
    }
    if(door[temp].num==0)
    {
        for(int i=temp+1;i<=n;i++)
            if(door[i].num==0)
            {
                door[i].num=temp;//故下标不能从0开始 
                door[temp].num=i;
                npair(temp+1);
                door[i].num=0;//回到未配对的情况 
                door[temp].num=0;
            } 
    }
    else
        npair(temp+1);
    return ; 
} 
int main () {
    FILE *fin=fopen("wormhole.in","r");
    FILE *fout=fopen("wormhole.out","w");
    int i,j;
    fscanf(fin,"%d",&n);
    for(i=1;i<=n;i++)
        {
            fscanf(fin,"%d%d",&door[i].x,&door[i].y);
            door[i].in=0;
            door[i].out=0;
        }
    sort(&door[1],&door[n+1],st);
    npair(1);
    fprintf(fout,"%d\n",sum);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值