1. 程式人生 > >【NOIP2018模擬賽2018.10.22】pets

【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
*/