NKOI 3747 宠物

Description
xxxxxyt学姐经常一个人在家,难免会感到寂寞,于是学姐养了n只可爱的宠物,比
如皮皮虾、大蟒蛇、藏狐、安康鱼…但即便如此学姐还是感到无聊。突然有一天,学姐想到
了让宠物们互相对战的消遣方法(请不要给动物保护协会打电话!)。学姐让宠物们两两进
行对战,n*(n-1)/2场对战后,学姐得到了一张相生相克图,然后又根据自己的喜好,把
宠物们分成了一队与二队。就在队伍分好后,学姐的强迫症又犯了,她希望自己的两支队伍
都满足这样一个性质:存在某种排列,使得排在后面的宠物能够击败排在前面的所有宠物。
但学姐的懒惰大家都是知道的,所以她找到了你,希望你能告诉她这两支队伍是否均满足要
求,如果是,她还希望你告诉她最多可以从二队中抽出多少只宠物放在一队,使得两支队伍
仍然满足要求。努力解决问题吧,而xxxxxyt学姐,瘫躺。

Input
第一行输入两个数字n和m,分别表示学姐有n只宠物,其中被分到一队的宠物有m
只。
接下来n行每行n个数字,ai,j
表示第i只宠物是否能战胜第j只宠物,保证ai,i=0
且ai,j=!aj,i

接下来一行m个数字,表示有哪些宠物被分到了一队。

Output
如果两支队伍均不能让xxxxxyt满意,则输出“NO”;否则输出“YES”,并输出一个
最大的k,使得从二队中非任意地抽出k只宠物放入一队后,两支队伍仍然满足条件。详细
格式见样例输出。

Sample Input 1
3 2
0 1 1
0 0 1
0 0 0
3 1

Sample Output 1
YES 1
Sample Input 2
4 3
0 1 0 1
0 0 1 1
1 0 0 1
0 0 0 0
1 2 3

Sample Output 2
NO
Sample Input 3
4 2
0 1 0 1
0 0 1 1
1 0 0 1
0 0 0 0
1 2

Sample Output 3
YES 1
Hint
注意:
宠物们的实力是相对的,也就是可能会出现A战胜B,B战胜C,C又战胜A的
情况。

数据范围:
20%的数据1<=m<n<=10
60%的数据1<=m<n<=100

100%的数据1<=m<n<=100


显然一个序列的规则是满足拓扑排序的,故可以用拓扑排序check,且合法的排列方法唯一,即拓扑序。现在问题就变成了有一队二队两条有向链,问二队链中至多有多少个可以合法地插入一队链,用f[i][j]表示一队链讨论到第i个二队
链讨论到第j个,二队链中至多插入f[i][j]个,转移为:

f[i][j]=max{f[i-1][j],f[i][j-1],f[i][j-1]+1(二队中第j个宠物弱于一队中1...i强于i+1...m)}
直接转移暴力判断复杂度为O(n^3)
因此对二队中第j个宠物是否弱于一队中1...i强于i+1...m进行预处理,便可在复杂度O(n2)下得到答案

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=1005,inf=1e9;
inline void _read(int &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
int n,m,type[maxn],du[maxn],f[maxn][maxn];
int s1[maxn],s2[maxn],l[maxn][maxn],r[maxn][maxn];
int map[maxn][maxn];
bool topsort(int s[],int kind,int sum){
	queue<int>q;
	int i,j,t=0;
	bool mark[maxn];
	memset(mark,0,sizeof(mark));
	for(i=1;i<=n+m;i++)
	    if(type[i]==kind&&(!du[i])){
	    	q.push(i);
	    	s[++t]=i;
	    	mark[i]=1;
		}
	while(q.size()){
		int x=q.front();q.pop();
		for(i=1;i<=n+m;i++)
		    if(!mark[i]&&type[i]==kind){
		    	if(map[x][i]&&du[i])du[i]--;
		    	if(!du[i]){
		    		q.push(i);
		    		mark[i]=1;
		    		s[++t]=i;
				}
			}
	}
	return t==sum;
}
int main(){
	int i,j,x;
	_read(n);_read(m);
	for(i=1;i<=n;i++){
		for(j=1;j<=n;j++)_read(map[i][j]);
		type[i]=2;
	}
	for(i=1;i<=m;i++){
		_read(x);
		type[x]=1;
	}
	for(i=1;i<=n;i++)
	    for(j=1;j<=n;j++)
	        if(type[i]==type[j]&&map[i][j])du[j]++;
	n-=m;swap(n,m);   
	if(!topsort(s1,1,n)||(!topsort(s2,2,m))){
		puts("NO");
		return 0;
	}
	for(j=1;j<=m;j++){
		l[0][j]=r[n+1][j]=1;
		for(i=1;i<=n;i++)l[i][j]= map[s1[i]][s2[j]]&&l[i-1][j];
		for(i=n;i;i--)r[i][j]= map[s2[j]][s1[i]]&&r[i+1][j];
	}
	for(i=0;i<=n;i++)
	    for(j=0;j<=m;j++){
	    	if(j)f[i][j]=max(f[i][j],f[i][j-1]);
	    	if(i)f[i][j]=max(f[i][j],f[i-1][j]);
	    	if(j&&l[i][j]&&r[i+1][j])f[i][j]=max(f[i][j],f[i][j-1]+1);
		}
	cout<<"YES"<<" "<<f[n][m];
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值