HDU6370-2018Multi-UniversityTrainingContest6-Werewolf-思维题

(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

目录

题意:传送门

 原题目描述在最下面。
 一个以狼人杀为背景的游戏(hhh)。题目保证村民一定说真话,狼人可能说真话也可能说假话。
 n个人,每个人人指认另一个人的身份,狼或村民。问你一定能确定多少个人一定是村民,多少个人一定是狼。

思路:

 贼有意思的一道题,打比赛时和同学一起讨论了很多情况,终于有了思路,最后彪神抬了一手,终AC。
 思路大概是这样的。

  • 任何情况都不能确定谁是村民!
  • 因为有狼这个不定因素。狼的话可真可假,也就是说可能说有人都是狼,也不会出现矛盾。
  • 在草稿纸列举出两个人互相指认的4种情况,只有一种情况能确定一个人一定是狼。
  • 栗子: A A 认为B是村民, B B 认为A是狼。这时 A A 一定是狼!
  • 反证法:如果A是村民,说真话,则 B B 也是村民,继续可得出A是狼,矛盾!故这时 A A 一定是狼。
  • 以此类推可得出规律,只有当1认为 2 2 是村民,2认为 3 3 是村民,3认为 4 4 是村民... n1 n − 1 认为 n n 是村民,n认为 i(1in1) i ( 1 ≤ i ≤ n − 1 ) 是狼时,可以确定 i i 一定是狼!(省略号里全部是指认别人为村民)
  • 有了这个谬论可以大胆敲代码。只要我们能确定一个人是狼,那么逆推,认为这个人是村民的人也肯定是狼!
  • A指认 B B ,就A B B <script type="math/tex" id="MathJax-Element-25">B</script>连边。把边标号,指认是狼就标号为1.
  • 当存在一个环,且环中只有一条标号为1的边,则那个被指认为狼的人一定是狼!
  • 我的解法是先拓扑排序给点打标记。未被打标记的点一定时环上的点。然后跑环检查狼的突破口。最后逆推所有狼。
  • 至此,本题结束。

AC代码:
#include<bits/stdc++.h> 
#define mme(a,b) memset((a),(b),sizeof((a)))  
using namespace std;
typedef long long LL;
const int N = 100005;
struct lp{
    int v,w,nex,id;
}cw[N*10],rev[N*10];//正向图和反向图的链式前向星
int n;
int revhead[N];
int head[N],tot;
int in[N];//入度
int vis[N], cnt[N];//是否被访问过
int is[N];//是否是狼
void init(){//初始化函数
  mme(head,-1);mme(in,0);
  mme(revhead,-1);mme(cnt,0);mme(is,0);
  tot=-1;
}
void add_edge(int u,int v,int id){//加边
  cw[++tot].v=v;cw[tot].nex=head[u];cw[tot].id=id;
  head[u]=tot;
  rev[tot].v=u;rev[tot].nex=revhead[v];rev[tot].id=id;
  revhead[v]=tot;
}
void tuopu(){//拓扑排序只是为了找出环
  queue<int>Q;
  for(int i=1;i<=n;++i){
    if(in[i]==0){
      Q.push(i);
    }
  }
  mme(vis,0);
  while(!Q.empty()){
    int u = Q.front();Q.pop();
    vis[u]=1;
    for(int i=head[u];~i;i=cw[i].nex){
      int v = cw[i].v;
      in[v]--;
      if(in[v]==0){
        Q.push(v);
      }
    }
  }
}
void dfs(int u,int Fa){//反着搜索找出所有的狼
  is[u]=1;
  for(int i=revhead[u];~i;i=rev[i].nex){
    int v = rev[i].v,id=rev[i].id;
    if(id==0){//id为0表示认为他是村民。而认为狼是村民的人肯定是狼
      dfs(v,u);
    }
  }
}
int num,p;
void find(int u,int Fa){//找出一定是狼的突破点
  cnt[u]=1;
  for(int i=head[u];~i;i=cw[i].nex){
    int v = cw[i].v,id=cw[i].id;
    num+=cw[i].id;
    if(id==1)p=v;//被指向的人是狼
    if(vis[v]==0&&cnt[v]==0){//跑环上的点且未被访问过
      find(v,u);
    }
  }
}
int main(){
  int T, a, c;
  scanf("%d", &T);
  while(T--) {
    scanf("%d", &n);
    init();
    char s[15];
    for(int i = 1; i <= n; i++) {
      c=0;
      scanf("%d%s",&a,s);
      if(s[0]=='w')c=1;//给边标号,指认狼的边标号为1
      add_edge(i,a,c);
      in[a]++;
    }
    tuopu();
    for(int i=1;i<=n;++i){
      if(vis[i]==0&&cnt[i]==0){//跑环找肯定是狼的突破点
        num=0;
        find(i,0);
        if(num==1)is[p]=1;//只能有一条边为指认狼,被指向的人是狼
      }
    }
    for(int i=1;i<=n;++i){
      if(is[i]==1){//反着搜索找出所有的狼
        dfs(i,0);
      }
    }
    int ans=0;
    for(int i=1;i<=n;++i){
      ans+=is[i];
    }
    printf("0 %d\n", ans);
  }
  return 0;
}


原题目描述:

Problem Description
“The Werewolves” is a popular card game among young people.In the basic game, there are 2 different groups: the werewolves and the villagers.

Each player will debate a player they think is a werewolf or not.

Their words are like “Player x is a werewolf.” or “Player x is a villager.”.

What we know is :

  1. Villager won’t lie.

  2. Werewolf may lie.

Of cause we only consider those situations which obey the two rules above.

It is guaranteed that input data exist at least one situation which obey the two rules above.

Now we can judge every player into 3 types :

  1. A player which can only be villager among all situations,

  2. A player which can only be werewolf among all situations.

  3. A player which can be villager among some situations, while can be werewolf in others situations.

You just need to print out the number of type-1 players and the number of type-2 players.

No player will talk about himself.

Input
The first line of the input gives the number of test cases T.Then T test cases follow.

The first line of each test case contains an integer N,indicating the number of players.

Then follows N lines,i-th line contains an integer x and a string S,indicating the i-th players tell you,”Player x is a S.”

limits:

1≤T≤10

1≤N≤100,000

1≤x≤N

S∈ {“villager”.”werewolf”}

Output
For each test case,print the number of type-1 players and the number of type-2 players in one line, separated by white space.

Sample Input

1
2
2 werewolf
1 werewolf

Sample Output

0 0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值