北京集训TEST12——PA( Mortal Kombat)

题目:

Description

      有一天,有N个外星人企图入侵地球。地球派出全球战斗力最强的M个人代表人类对抗外星人。根据外星的战斗规则,每个外星人应该分别与一名地球人对战(不同的外星人要与不同的地球人对战)。如果任意一个外星人获胜,那么地球将被外星人占领。

      幸运的是,人类可以决定对战顺序,且可以决定每次对战的两名战士,但是要保证符合外星的战斗规则。

      地球有一个保护神。他能提前预知每一名地球人和每一名外星人的战斗结果。在战争开始前,保护神必须确定第一场战斗的两名战士。举个例子:假设第一场为人类A对战外星人A,但是人类A是能打败外星人B的唯一一名战士,那么即使人类A打败了外星人A,也不可避免地导致地球被外星人占领,因为外星人B将会打败他的对手。这意味着:第一场战斗中,“人类A对战外星人A”这个组合是不能选的。

      你的任务是:找出所有在第一场战斗中不能选的组合,即选择该组合会不可避免地导致地球被外星人占领。

Input

      第一行为两个整数 N,M(1N300,NM1500) 。

      接下来N行M列是一个二进制矩阵A。 Ai,j=1 当且仅当第j个地球人能战胜第i个外星人。

Output

      输出一个N行M列的矩阵B。若第一场战斗中,“第i个外星人与第j个地球人”是不能选的组合, Bi,j 应为1,否则 Bi,j 应为0。

Sample Input

【样例输入1】
4 4
1111
1000
1111
1111
【样例输入2】
4 5
10000
10000
10000
10000
【样例输入3】
4 4
1111
1110
1100
1000

Sample Output

【样例输出1】
1000
0111
1000
1000
【样例输出2】
11111
11111
11111
11111
【样例输出3】
1110
1101
1011
0111

HINT

 

【数据范围与约定】

      子任务1(10分): N=4,M=6

      子任务2(20分): N=15,M=30

      子任务3(25分): N=300,M=300

      子任务4(45分): N=300,M=1500


题解:

解法:二分图最大匹配+Tarjan

首先,题意可以转化为:去掉第i行和第j列后,判断二分图是否存在满匹配。直接暴力做可以得到30分。

更进一步,就是判断边(i, j)是否为该二分图最大匹配的匹配边。

考虑n = m的情况。我们可以先跑一次最大匹配,将匹配边从左向右连,非匹配边从右向左连。对新的图跑Tarjan,若左边的点i与右边的点j处于同一个强联通分量中,边(i, j)一定是最大匹配的匹配边(理由是一个强联通分量中,必能从该点出发又回到该点,而这个路线恰好是匹配边-非匹配边-匹配边……)。

对于m > n的情况,把外星人补成m个,就可以转化为n = m的情况了。

 


 

心得:

  以前匈牙利算法只会求其中一种情况,这个问题相当于求最大匹配的所有解:将两边数字同一跑匈牙利后跑tarjian;

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int M=3005;
int tot,fir[M],nxt[M*M],go[M*M];
int belon[M];
int n,m;
int id[M],dfs[M],low[M],stack[M],top,sumid,cnt;
bool visit[M];
bool insta[M];
char ma[M][M];
inline void comb(int u,int v)
{
  nxt[++tot]=fir[u],fir[u]=tot,go[tot]=v;
}
bool find(int x)
{
  for(int i=1;i<=m;i++)
  {
    if(ma[x][i]=='1'&&!visit[i])
    {
      visit[i]=true;
      if(!belon[i]||find(belon[i]))
      {
        belon[i]=x;
        return true;
      }
    }
  }
  return false;
}
inline void tarjian(int u)
{
  dfs[u]=low[u]=++cnt;
  stack[++top]=u;
  insta[u]=true;
  for(int e=fir[u];e;e=nxt[e])
  {
    int v=go[e];
    if(!dfs[v])
    {
      tarjian(v);
      low[u]=min(low[u],low[v]);
    }
    else 
      if(insta[v])
        low[u]=min(low[u],dfs[v]);
  }
  if(low[u]==dfs[u])
  {
    sumid++;
    while(stack[top]!=u)
    {
      insta[stack[top]]=false;
      id[stack[top]]=sumid;
      top--;
    }
    insta[stack[top]]=false;
    id[stack[top]]=sumid;
    top--;
  }
}
int main()
{
  freopen("a.in","r",stdin);
  scanf("%d%d",&n,&m); 
  
  for(int i=1;i<=n;i++)
    scanf("%s",ma[i]+1);
  for(int i=n+1;i<=m;i++)
    for(int j=1;j<=m;j++)
      ma[i][j]='1';
  
  int temp=0;
  for(int i=1;i<=n;i++)
  {
    memset(visit,false,sizeof(visit));
    if(find(i))  temp++;
  }
  if(temp!=n)
  {
  
    for(int i=1;i<=n;i++)
    {  
      for(int j=1;j<=m;j++)
        cout<<"1";
      cout<<endl;
    }
    return 0;
  }
  else 
  {  
    int k=n+1;
    for(int i=1;i<=m;i++)
    {
      if(!belon[i])
        belon[i]=k++;
    }
  }
  for(int i=1;i<=m;i++)
    for(int j=1;j<=m;j++)
      if(ma[i][j]=='1')
      {
        if(belon[j]==i)  comb(i,j+m);
        else comb(j+m,i);
      }
  for(int i=1;i<=m*2;i++)
    if(!dfs[i])  tarjian(i); 
  for(int i=1;i<=n;i++)
  {  
    for(int j=1;j<=m;j++)
    {
      if(ma[i][j]=='1'&&(belon[j]==i||id[i]==id[j+m]))
        cout<<"0";
      else cout<<"1";
    }
    cout<<endl;
  }
  return 0;
}

 

 

转载于:https://www.cnblogs.com/AseanA/p/6591064.html

好的,你的问题是关于一个层次结构的定义。这里给出一个 Java 代码示例: ```java // 定义人,具有基本的吃和睡行为 class Human { public void eat() { System.out.println("Human eat"); } public void sleep() { System.out.println("Human sleep"); } } // 定义可以做梦的行为物体 interface Dreamable { public void dream(); } // 定义可以飞行的行为物体 interface Flyable { public void fly(); } // 定义超人,继承自人,具有飞行技能 class Superman extends Human implements Flyable { public void fly() { System.out.println("Superman fly"); } } // 定义凡人,继承自人,具有做梦技能 class Mortal extends Human implements Dreamable { public void dream() { System.out.println("Mortal dream"); } } // 定义大鸟实现飞行接口 class Eagle implements Flyable { public void fly() { System.out.println("Eagle fly"); } } // 测试 public class Test { public static void main(String[] args) { // 构建凡人对象 Mortal mortal = new Mortal(); mortal.eat(); mortal.sleep(); mortal.dream(); // 构建会飞的对象数组 Flyable[] flyables = new Flyable[] {new Eagle(), new Superman()}; // 打印数组中各对象的状态 for (Flyable f : flyables) { if (f instanceof Eagle) { System.out.println("This is an Eagle."); } else if (f instanceof Superman) { System.out.println("This is a Superman."); } f.fly(); } } } ``` 注意,在 `Test` 的 `main` 方法中,我们首先构建了一个凡人对象 `mortal`,并调用了其吃、睡、做梦的方法。然后,我们构建了一个会飞的对象数组 `flyables`,其中包含一个大鸟对象和一个超人对象。我们通过遍历数组中的每个元素,并使用 `instanceof` 运算符判断其型,来打印数组中各对象的状态。最后,我们调用每个对象的飞行方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值