百日训练(第三天)

本文介绍了如何在洛谷编程竞赛中解决一道关于两只塔姆沃斯牛的问题,涉及转向逻辑和避免牛相遇的策略,以及使用专属值方法判断路径循环。还讨论了一种基于卡特兰数的数论问题,通过递推公式求解特定序列的合法数量。
摘要由CSDN通过智能技术生成

至于为什么没有第二天,因为昨天的题不需要补。

洛谷 —— 两只塔姆沃斯牛 The Tamworth Two

P1518 [USACO2.4] 两只塔姆沃斯牛 The Tamworth Two - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这道题,我们需要解决两个难点

1、如何转向 ?

我们可以定义一个数组f[3]表示人的状态,f[0] = 0表示向北 , 1表示向东,2,3依次表示向南和西

那么牛也是同理,当我们遇到障碍或者说要越界的时候,就把这个状态改变,那么代码如下

void move(int x , int y , int dir , int obj){ // dir表示方向,obj表示人或牛0是人,1是牛
  if(dir == 0){
    if(s[x-1][y]=='*' || x-1 < 1){  //改变方向
      if(obj == 0){ //人
        f[0] = 1;
      }
      else c[0] = 1;
    }
    else{ //不改变方向
      if(obj == 0)f[1]--;
      else c[1]--;
    }
  }
  if(dir == 1){
    if(s[x][y+1]=='*' || y+1 >10){  //改变方向
      if(obj == 0){ //人
        f[0] = 2;
      }
      else c[0] = 2;
    }
    else{ //不改变方向
      if(obj == 0)f[2]++;
      else c[2]++;
    }
  }
  if(dir == 2){
    if(s[x+1][y]=='*' || x+1 >10){  //改变方向
      if(obj == 0){ //人
        f[0] = 3;
      }
      else c[0] = 3;
    }
    else{ //不改变方向
      if(obj == 0)f[1]++;
      else c[1]++;
    }
  }
  if(dir == 3){
    if(s[x][y-1]=='*' || y-1 <1){  //改变方向
      if(obj == 0){ //人
        f[0] = 0;
      }
      else c[0] = 0;
    }
    else{ //不改变方向
      if(obj == 0)f[2]--;
      else c[2]--;
    }
  }
}

2、第二个难点是如何才能永远不相遇,当形成环的时候就不会相遇了,也就是当他们先后两次从同一个方向走到同一个地点,此时,就会陷入死循环。那么如何判断呢?这里介绍一种较为古老的方法,本人第一次接触到这种方法时是在牛客周赛。就是专属值的方法。

假设说有两个坐标(1,2)和(2,1),我们可以让第一个值的权重为1 , 第二个值的权重为10,这样第一个坐标的专属值就是1 + 2*10 = 21 , 第二个就是2 +10 =12,每个坐标都有对应的专属值,那么这里也是如此

tdz = f[1] + f[2]*10 + c[1]*100 + c[2]*1000 + f[0]*10000 + c[0]*100000;//专属值
  if(zt[tdz]){   //zt为布尔类型
    cout<<0<<endl;
    return;
  }

那么完整代码如下

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define INF 0x3f3f3f3f
const double pi = 3.14;
// #define x first
// #define y second 
#define int long long
// #define ll long long
typedef pair<int,int> pii;
// const int mod = 998244353;
const int mod = 1e9+7;
const int N = 6e5+9;
char s[12][12];
int f[3] , c[3];
int ans;
bool zt[N];
int tdz;
//0 1 2 3 北 东 南 西
void move(int x , int y , int dir , int obj){
  if(dir == 0){
    if(s[x-1][y]=='*' || x-1 < 1){  //改变方向
      if(obj == 0){ //人
        f[0] = 1;
      }
      else c[0] = 1;
    }
    else{ //不改变方向
      if(obj == 0)f[1]--;
      else c[1]--;
    }
  }
  if(dir == 1){
    if(s[x][y+1]=='*' || y+1 >10){  //改变方向
      if(obj == 0){ //人
        f[0] = 2;
      }
      else c[0] = 2;
    }
    else{ //不改变方向
      if(obj == 0)f[2]++;
      else c[2]++;
    }
  }
  if(dir == 2){
    if(s[x+1][y]=='*' || x+1 >10){  //改变方向
      if(obj == 0){ //人
        f[0] = 3;
      }
      else c[0] = 3;
    }
    else{ //不改变方向
      if(obj == 0)f[1]++;
      else c[1]++;
    }
  }
  if(dir == 3){
    if(s[x][y-1]=='*' || y-1 <1){  //改变方向
      if(obj == 0){ //人
        f[0] = 0;
      }
      else c[0] = 0;
    }
    else{ //不改变方向
      if(obj == 0)f[2]--;
      else c[2]--;
    }
  }
}



bool end(){
  if(f[1] == c[1] && f[2] == c[2])return 0;
  else return 1;
}


void solve(){
  int xx = 0 , yy = 0 ,x = 0 , y = 0; 
 for(int i =1;i<=10;++i){
  for(int j =1;j<=10;++j){
    cin>>s[i][j];
    if(s[i][j] =='C'){
      f[1] = i;
      f[2] = j;
    }
    if(s[i][j] =='F'){
      c[1] = i;
      c[2] = j;
    }
  }
 }
 while(end()){
  tdz = f[1] + f[2]*10 + c[1]*100 + c[2]*1000 + f[0]*10000 + c[0]*100000;
  if(zt[tdz]){
    cout<<0<<endl;
    return;
  }
  zt[tdz] =1 ;
  move(f[1] , f[2] , f[0] , 0); //人
  move(c[1] , c[2] , c[0] , 1); 
  ans++;
 }
 cout<<ans;

 

  


}


signed main(){
    ios::sync_with_stdio(false);
    cout.tie(0);
    cin.tie(0);
    int _  = 1;
	
    while(_--){
      solve();
    }
    
    return 0;
}

洛谷 —— 栈

这题是一个数论中的内容 : 卡特兰数。对于这个大家可以上网搜一下相关的论文,博客来看,在这里简单说一下,卡特兰数

我们可以把进栈理解为 +1 , 出栈为-1 , 那么对于每一个-1,在他之前必须有大于等于1个+1 , 才能让-1生效, 否则就不合法,因为栈为空了,如何-1呢? 所以对于任意一个前缀,一定会大于等于0。那么如果总数是2n的话,最多可以有n个数字出栈,那么出栈的总数就是_{}^{}\textrm{}C2n n , 非法的出栈总数是C2n n+1,因此合法的出栈顺序就是C2n n - C2n n+1,卡特兰通项就是C2n n/ (n+1)。 

那么我们知道f(n) = C(2n , n)/(n+1);

就可以求递推公式了,根据推导可以得到  f[n+1] = f[n] * (4n - 2) / n +1;

之后就可以解题了

dp[1] = 1;
for(int i =2;i<=n;++i){
    dp[i] = dp[i-1] * (4 *i -2)/(i-1);
}
cout<<dp[n];

完整代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define INF 0x3f3f3f3f
const double pi = 3.14;
// #define x first
// #define y second 
#define int long long
// #define ll long long
typedef pair<int,int> pii;
// const int mod = 998244353;
const int mod = 1e9+7;
const int N = 20 , M = 2e5;
int dp[N];
int n;

void solve(){
  cin>>n;
  dp[1] =1;
  for(int i =2;i<=n;++i){
    dp[i] = dp[i-1]*(4*i-2)/(i+1);
  }
  cout<<dp[n];


  


}


signed main(){
    ios::sync_with_stdio(false);
    cout.tie(0);
    cin.tie(0);
    int _  = 1;
	
    while(_--){
      solve();
    }
    
    return 0;
}


  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值