【NOIP2018模拟赛2018.10.22】pets

与cards同天考的题,反正很恶心人。。

首先分组,我将一队放进a,二队放进b,然后队伍中n^2建边,若 i 打得过 j 就连一条有向边,将 j 的入度+1,然后topo序判环。

可以看出如果有环就说明一个队中存在 a 打得过 b,b 打得过 c, c 又打得过 a 的情况,这种情况出现是不可能保证后面的pets都能打得过前面的pets的,故不合法,输出NO。

若没有环,这时候我们得到俩个有序链,用一个线性dp:f[i][j] 表示 a 链处理到 i 个pets时,b 链处理到 j 个pets时的最大 b 加入 a pets数。

转移方程跟最长公共上升子序列很像:f[i][j] = max(max(f[i][j-1],f[i-1][j]),f[i][j-1]( 这里的前提是 bj 强于a1 ~ ai 且 bj 弱于 ai+1~an))

朴素DP为n^3做法,60分。

正解是预处理 bj 强于 1~? 弱于 ? ~ n,如此就不用多枚举一维了。

那么我们用一个 l[i][j] 表示 bj 是否强于1~ai,用一个 r[i][j] 表示 bj 是否弱于 ai ~ n

那么在转移时直接看 l[i][j] && r[i+1][j] 是否为真就好了,为真就可以转移,否则就不行。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e3 + 5;
#define ll long long
#define pt putchar
#define ex pt('\n')
#define ko pt(' ')
int vs[MAXN][MAXN],color[MAXN],x;
int n,m,a[MAXN],b[MAXN],la = 0,lb = 0;
int cnt = 0,head1[MAXN<<1],head2[MAXN<<1];
bool l[MAXN][MAXN],r[MAXN][MAXN];
int f[MAXN][MAXN];
int ing[MAXN];
struct edge
{
	int next,to;
}ea[MAXN<<1],eb[MAXN<<1];
void add1(int u,int v)
{
	ea[++cnt].next = head1[u]; ea[cnt].to = v; head1[u] = cnt;
}
void add2(int u,int v)
{
	eb[++cnt].next = head2[u]; eb[cnt].to = v; head2[u] = cnt;
}
void in(int &x)
{
	int num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = getchar();}
	x = num*f;
}
void out(int x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x%10 + '0');
}

int q[MAXN<<2];
bool vis[MAXN];

bool toposort(int s[],int kind,int sum)
{
	memset(q,0,sizeof q);
	memset(vis,0,sizeof vis);
	int h = 0,t = 0,len = 0;
	for(int i = 1;i <= n+m;i++)
		if(color[i] == kind && !ing[i])
			q[++t] = i,s[++len] = i,vis[i] = 1;
	while(h < t)
	{
		int x = q[++h];
		for(int i = 1;i <= n+m;i++)
			if(!vis[i] && color[i] == kind)
			{
				if(vs[x][i] && ing[i]) ing[i]--;
				if(!ing[i])
					q[++t] = i,vis[i] = 1,s[++len] = i;
			}
	}
	if(len == sum) return 1;
	else return 0;
}

int main()
{
	in(n); in(m);
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= n;j++)
			in(vs[i][j]),color[i] = 2;
	for(int i = 1;i <= m;i++) 
		in(x),color[x] = 1;	
	for(int i = 1;i <= n;i++) 
		for(int j = 1;j <= n;j++)
			if(color[i] == color[j] && vs[i][j]) ing[j]++;
	n -= m; swap(n,m);
	if(!toposort(a,1,n) || !toposort(b,2,m)) {cout << "NO"; return 0;}
	cout << "YES" << ' ';
	for(int j = 1;j <= m;j++)
	{
		l[0][j] = r[n+1][j] = 1;
		for(int i = 1;i <= n;i++) l[i][j] = vs[a[i]][b[j]] && l[i-1][j];
		for(int i = n;i;i--) r[i][j] = vs[b[j]][a[i]] && r[i+1][j];
	} 
	for(int i = 0;i <= n;i++)
		for(int 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);
		}
	out(f[n][m]);
	return 0; 
}
/*
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
*/

猜你喜欢

转载自blog.csdn.net/chang_yl/article/details/83279079