USACO Wormholes(模拟)

题目请点我
题解:
这道题思路很简单,就是简单的深搜,找出所有的组合,然后判断能否成环。关键在于如何判断能否成环,我的思路是利用递归模拟,看能否第二次经过某一个点。中间也出现了错误,首先,每次访问的下一个点应该是同一行上当前点右边的第一个点;其次,某个点被访问过必须是作为起点被访问过,而不仅仅是到达。
代码实现:

/*
ID: eashion
LANG: C++
TASK: wormhole
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define MAX 13
#define INF 0x7fffffff

using namespace std;

struct node{
    int x,y;
};

int N;
int res;
int used[MAX];
//配对信息
int Pair[MAX];
//点集
node Nlis[MAX];
//测试是否成环
bool test();
//debug打印输出结果
void print();
void dfs(int pos);
//递归
bool if_sucked(int pos);
int main()
{
    freopen("wormhole.in","r",stdin);
    freopen("wormhole.out","w",stdout);
    while( scanf("%d",&N) != EOF ){
        res = 0;
        memset(used,0,sizeof(used));
        memset(Pair,-1,sizeof(Pair));
        for( int i = 0; i < N; i++ ){
            scanf("%d%d",&Nlis[i].x,&Nlis[i].y);
        }
        dfs(0);
        printf("%d\n",res);
    }
    return 0;
}

void dfs( int pos ){
    if( pos == N ){
        if( test() ){
            res++;
            //print();
            //快速debug,定位到合理解再进行一次测试
            //test();
        }
        return ;
    }
    if( Pair[pos] == -1 ){
        for( int i = pos+1; i < N; i++ ){
            if( Pair[i] == -1 ){
                Pair[i] = pos;
                Pair[pos] = i;
                dfs(pos+1);
                Pair[i] = -1;
                Pair[pos] = -1;
            }
        }
    }
    else{
        dfs(pos+1);
    }
    return ;
}

bool test(){
    for( int i = 0; i < N; i++ ){
        memset(used,0,sizeof(used));
        if( if_sucked(i) ){
            return true;
        }
    }
    return false;
}

bool if_sucked(int pos){
    bool flag = false;
    //这里仅标记每一次的起点被访问过
    used[pos] = 1;
    //used[Pair[pos]] = 1;
    int x = Nlis[Pair[pos]].x;
    int y = Nlis[Pair[pos]].y;
    int dis = INF;
    int in = -1;
    //找到右边第一个点
    for( int i = 0; i < N; i++ ){
        if( Nlis[i].x > x && Nlis[i].y == y ){
            if( Nlis[i].x-x < dis ){
                dis = Nlis[i].x-x;
                in = i;
            }
        }
    }
    if( in != -1 ){
        if( used[in] == 1 ){
            flag = true;
        }
        else{
            flag = if_sucked(in);
        }
    }
    return flag;
}

void print(){
    for( int i = 0; i < N; i++ ){
        printf("%d %d\n",i,Pair[i]);
    }
    printf("\n");
}

还有例程上的解法,很巧妙,类似于并查集的感觉。


#include <iostream>
#include <fstream>
using namespace std;
#define MAX_N 12

int N, X[MAX_N+1], Y[MAX_N+1];
int partner[MAX_N+1];
int next_on_right[MAX_N+1];

bool cycle_exists(void)
{
  for (int start=1; start<=N; start++) {
    // does there exist a cylce starting from start
    int pos = start;
    for (int count=0; count<N; count++)
      pos = next_on_right[partner[pos]];
    if (pos != 0) return true;
  }
  return false;
}

// count all solutions
int solve(void) 
{
  // find first unpaired wormhole
  int i, total=0;
  for (i=1; i<=N; i++) 
    if (partner[i] == 0) break;

  // everyone paired?
  if (i > N) {
    if (cycle_exists()) return 1;
    else return 0;
  }

  // try pairing i with all possible other wormholes j
  for (int j=i+1; j<=N; j++)
    if (partner[j] == 0) {
      // try pairing i & j, let recursion continue to 
      // generate the rest of the solution
      partner[i] = j;
      partner[j] = i;
      total += solve();
      partner[i] = partner[j] = 0;
    }
  return total;
}

int main(void)
{
  ifstream fin("wormhole.in");
  fin >> N;
  for (int i=1; i<=N; i++) fin >> X[i] >> Y[i];
  fin.close();

  for (int i=1; i<=N; i++) // set next_on_right[i]...
    for (int j=1; j<=N; j++)
      if (X[j] > X[i] && Y[i] == Y[j]) // j right of i...
    if (next_on_right[i] == 0 ||
        X[j]-X[i] < X[next_on_right[i]]-X[i])
      next_on_right[i] = j;

  ofstream fout("wormhole.out");
  fout << solve() << "\n";
  fout.close();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值