【NOIP2018模擬賽2018.10.22】pets
阿新 • • 發佈:2018-11-06
與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 */