HDU 5811 Colosseo (拓扑排序+LIS)

Colosseo

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 530    Accepted Submission(s): 15

Problem Description
Mr. Chopsticks keeps N monsters, numbered from 1 to N. In order to train them, he holds N * (N - 1) / 2 competitions and asks the monsters to fight with each other. Any two monsters fight in exactly one competition, in which one of them beat the other. If monster A beats monster B, we say A is stronger than B. Note that the “stronger than” relation is not transitive. For example, it is possible that A beats B, B beats C but C beats A.

After finishing all the competitions, Mr. Chopsticks divides all the monsters into two teams T1 and T2, containing M and N – M monsters respectively, where each monster is in exactly one team. Mr. Chopsticks considers a team of monsters powerful if there is a way to arrange them in a queue (A1, A2, …, Am) such that monster Ai is stronger than monster Aj for any 1<=i<j<=m. Now Mr. Chopsticks wants to check whether T1 and T2 are both powerful, and if so, he wants to select k monsters from T2 to join T1 such that the selected monsters together with all the monsters in T1 can still form a powerful team and k is as large as possible. Could you help him?
Input
The input contains multiple test cases. Each case begins with two integers N and M (2 <= N <= 1000, 1 <= M < N), indicating the number of monsters Mr. Chopsticks keeps and the number of monsters in T1 respectively. The following N lines, each contain N integers, where the jth integer in the ith line is 1 if the ith monster beats the jth monster; otherwise, it is 0. It is guaranteed that the ith integer in the jth line is 0 iff the jth integer in the ith line is 1. The ith integer in the ith line is always 0. The last line of each case contains M distinct integers, each between 1 and N inclusively, representing the monsters in T1. The input is terminated by N = M = 0.

Output
For each case, if both T1 and T2 are powerful, output “YES” and the maximum k; otherwise, output “NO”.
Sample Input
  
  
3 2 0 1 1 0 0 1 0 0 0 3 1 4 3 0 1 0 1 0 0 1 1 1 0 0 1 0 0 0 0 1 2 3 4 2 0 1 0 1 0 0 1 1 1 0 0 1 0 0 0 0 1 2 0 0
Sample Output
  
  
YES 1 NO YES 1
Hint
In the third example, Mr. Chopsticks can let the monster numbered 4 from T2 join into T1 to form a queue (1, 2, 4).
Author
SYSU
Source
Recommend
wange2014   |   We have carefully selected several similar problems for you:  5831 5830 5829 5828 5827 
 
题意:有n只怪兽,让他们互相打一次,用n X n矩阵表示,在i th 行,j th列是1,就是怪兽 i 打爆了怪兽 j。
但这些关系没有传递性。比如不存在A能打败B ,B能打败C ,C能打败A的情况。
现在挑选出m只怪兽组成T1队,剩下n-m只怪兽组成T2队。
问你T1队与T2队是否存在某种排列 a1,a2,a3,...,ak ,使任意一只怪兽都能打败他右边所有怪兽。存在就YES,否则就NO。

如果输出的是YES。就在问你最多能从T2中能选出多少只怪兽插入到T1中,同样能满足上面的情况。

题解:(卡常数题,用gets才过)
第一问:可以用toposort来解决。
第二问:如果是YES,可以在拓扑排序中找到T1和T2的拓扑序列。在T2的拓扑序列中从低到高(最弱的怪兽到能打败T2中所有怪兽的怪兽)遍历,在T1中找到它可以放置的位置,并且这个位置是唯一的!否则就不能插入T1。
然后标上T2在T1中的位置值,对得到的这个位置值数列,求最长上升子序列的长度就是答案. 总复杂度O(N^2)。

官方题解:
判断YES或NO的时候拓扑排序或者暴力O(N^2)判断都可以. 接下来对于T2中的每个点,只需要在T1中扫一遍就可以判断出是否可以放进T1,如果不能放进去就直接丢掉,如果可以就确定放在哪个位置. 注意因为是个竞赛图,所以这个位置是唯一的. 给T2剩下的点按在T2中的拓扑顺序(因为是竞赛图,所以这个顺序也是唯一的)标上它们在T1中的位置值,对得到的这个位置值数列求最长上升子序列的长度就是答案. 总复杂度O(N^2).

AC代码:
#include<bits/stdc++.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<map>
#include<queue>
#include<set>
#include<stack>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define MP(x,y) make_pair(x,y)  
template <class T1, class T2>inline void getmax(T1 &a, T2 b) { if (b>a)a = b; }  
template <class T1, class T2>inline void getmin(T1 &a, T2 b) { if (b<a)a = b; }
int read()
{
	int v = 0, f = 1;
	char c =getchar();
	while( c < 48 || 57 < c ){
		if(c=='-') f = -1;
		c = getchar();
	}
	while(48 <= c && c <= 57) 
		v = v*10+c-48, c = getchar();
	return v*f;
}
char str[2333];
int mp[1110][1110];
int T1[1110];
int n,m;
int tp[2];
int vc[2][1110];
int indegree[1110];
int LIS[1110];

int toposort()
{
	memset(indegree,0,sizeof(indegree));
	int u,v;
	for(int i = 1; i<=n; i++)
	{
		for(int j= i+1; j<=n; j++)
		{
			if(T1[i] != T1[j] )continue;
			if(mp[i][j]) indegree[j]++;
			if(mp[j][i]) indegree[i]++;
		}
	}
	for (int i = 1; i <= n; i++)
	{

		for ( u = 1; u <= n; u++)
		{
			if(! indegree[u]) break;
		}

		if(u == n+1) return 0;

		indegree[u]--;

		vc[ T1[u] ][ tp[ T1[u] ]++ ] = u;

		for(v = 1; v <= n; v++ )
		{
			if(mp[u][v] && T1[u] == T1[v] )
			{
				indegree[v]--;
			}
		}

	}
	return 1;
}

void Search(int l,int &len,int x)
{
    int r = len;

    int ans = -1;
    while(l <= r)
    {
        int mid = (l+r)>>1;
        if(LIS[mid] <= x) l = mid+1;
        else
        {
            ans = mid;
            r = mid-1;
        }
    }
    if(ans == -1) ans = len++;
    LIS[ans] = x;
}

int solve()
{
    int len = 0;
    memset(LIS,0,sizeof(LIS));

    int u,v;
    for(int i = 0; i < tp[0]; i++)
    {
        int pos = 1;
        u = vc[0][i];

        for(int j = tp[1]-1; j >= 0; --j)
        {
            v = vc[1][j];
           // printf("%d->%d %d\n",v,u,mp[v][u]);
            if(mp[v][u])
            {
                if(pos <= 1) pos = j+2;
            }
            else if(pos != 1)
            {
                pos = -1;
                break;
            }
        }
        //printf("%d\n---------\n",pos);
        if(pos == -1) continue;
        Search(0,len,pos);
    }
    return len;
}
int main()
{
	//freopen("in.txt","r",stdin);
	 while(~scanf("%d%d\n",&n,&m))
	 {
	 	if(n==0&&m==0) break;

	 	tp[0] = tp[1] = 0;
	 	for(int i = 1; i <= n; i++)
	 	{
	 		gets(str);
	 		for(int j = 1; j <= n; j++)
	 		{
	 			char ch = str[2*j - 2];
	 			mp[i][j] = ch -'0';
	 		}
	 	}
	 	int x;
	 	memset(T1,0,sizeof(T1));
	 	for(int i = 0; i < m; i++)
	 	{
	 	    x=read();
	 		T1[x] = 1;
	 	}
	 	if( !toposort() ){
	 		puts("NO");
	 		continue;
	 	}
	 	else printf("YES %d\n", solve());
	 }
		return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值