【HDU 5811】Colosseo(拓扑+输入优化)

【HDU 5811】Colosseo(拓扑+输入优化)

Colosseo

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


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
 

题目大意:
Chopsticks养了n只怪兽。他们有制约关系,用一个矩阵 mat 表示。
matij=1 表示i怪物能打败j怪物,同时对称位置 matji=0
matij=0 表示i怪物打不过j怪物,同时对称位置 matji=1
matii=0

要注意制约关系不具备传递性,例如可以有A能打败B B能打败C C能打败A的情况。

现在Chop挑选出m只怪兽组成T1队,剩下n-m只怪兽组成T2队

询问T1队与T2队是否合法
合法是指存在某种排列 a1,a2,a3,...,ak 满足 mataiaj=1(i<j)
排列中任意一只怪兽都能打败他右边所有怪兽。

然后问最多从T2中能选出几只怪兽插入到T1中,能保证T1依旧合法?

第一个判断用拓扑排序即可,什么姿势都可以。

如果是YES,可以在拓扑排序中找到两个队伍的拓扑序。在T2的拓扑序列中从低到高(最弱的怪兽到能打败T2中所有怪兽的怪兽)遍历,在T1中找到它可以放置的位置,可知这个位置是固定的!,否则就不能放入T1

然后找到最长上升/下降子序列,这个取决于很多因素,比如拓扑序存储方式,遍历方式等等

然后测试通过,兴高采烈的提交…………………………
Surprise!!TLE……

憋动手。。。并不是算法有误,改一下输入试试。。。scanf(“%d”)1000*1000本题会爆炸(其实本地测后台数据并没爆……)
改成gets,然后从字符串中得到数字即可,因为只有1和0,用下标。

神题啊…………

代码如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const int mod = 1e9+7;
const double eps = 1e-8;

int mp[1111][1111];
int in[1111];
int team[1111];
int vc[2][1111];
int n,m,tp[2];

bool topo()
{
    memset(in,0,sizeof(in));
    int u,v;

    for(int i = 1; i <= n; ++i)
    {
        for(int j = i+1; j <= n; ++j)
        {
            if(team[i] != team[j]) continue;
            if(mp[i][j]) in[j]++;
            if(mp[j][i]) in[i]++;
        }
    }

    for(int i = 1; i <= n; ++i)
    {
        for(u = 1; u <= n; ++u)
            if(!in[u]) break;

        if(u == n+1) return false;
        in[u]--;
        vc[team[u]][tp[team[u]]++] = u;

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

    }

    return true;
}

int dp[1111];

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

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

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

    int u,v;
    for(int i = 0; i < tp[0]; ++i)
    {
        int pos = 1;
        u = vc[0][i];
        //printf("%d:\n",u);

        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;
}

//#include <ctime>
char str[2333];
int main()
{
    //double x = clock();
    //fread("1003.in");
    //fwrite("test.out");


    while(~scanf("%d%d\n",&n,&m) && (n+m))
    {
        tp[0] = tp[1] = 0;
        for(int i = 1; i <= n; ++i)
        {
            gets(str);
            for(int j = 1; j <= n; ++j)
            {
                char ch = str[j*2-2];
                mp[i][j] = ch-'0';
            }
        }

        int x;
        memset(team,0,sizeof(team));
        for(int i = 0; i < m; ++i)
        {
            scanf("%d",&x);
            team[x] = 1;
        }

        if(!topo())
        {
            puts("NO");
            continue;
        }
        printf("YES %d\n",solve());
    }

    //printf("%f\n",(clock()-x)/CLOCKS_PER_SEC);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值