HDU 5811 Colosseo(拓扑排序+单调DP)
【题目链接】 http://acm.hdu.edu.cn/showproblem.php?pid=5811
【题目大意】
给出 一张单向图,现在将其划分成了两个部分,问划分之后的点是否分别满足按照一定序列排序后后面的点可以直接与前面的点相连,如果可以,从第二部分拆出几个点到第一部分仍然满足这个性质。
【题解】
对于第一问,我们可以拓扑排序判断,对于第二问,我们发现先在第一组的序列中找到第二组每个点的可插入位置,在第二组的位置序列中做最长不下降子序列就是答案。
【代码】
#include <cstdio> #include <vector> #include <queue> #include <climits> #include <algorithm> using namespace std; const int N=1005,M=40000000; int n,m,mark[N],d[N],map[N][N],x,f[N][N],dp[N]; vector<int> T1,T2; char file[M],*F=file; void read(int&x){for(x=0;*F<48;F++);while(*F>47)x=x*10+*F++-48;} bool Topological(int t,vector<int>&ret){ vector<int> v; for(int i=0;i<n;i++){d[i]=0;if(mark[i]==t)v.push_back(i);} for(int i=0;i<v.size();i++){ for(int j=0;j<v.size();j++){ if(map[v[i]][v[j]])d[v[i]]++; } }queue<int>Q; for(int i=0;i<v.size();i++)if(!d[v[i]])Q.push(v[i]); while(!Q.empty()){ int u=Q.front();Q.pop(); ret.push_back(u); for(int i=0;i<v.size();i++){ if(map[v[i]][u]){ --d[v[i]]; if(!d[v[i]])Q.push(v[i]); } } }if(ret.size()!=v.size())return 0; return reverse(ret.begin(),ret.end()),1; } int main(){ fread(file,1,M,stdin); while(1){ read(n);read(m); if(m+n==0)return 0; for(int i=0;i<n;i++)for(int j=0;j<n;j++)read(map[i][j]); for(int i=0;i<n;i++)mark[i]=0; for(int i=0;i<m;read(x),mark[--x]=1,i++); T1.clear(); T2.clear(); if(!Topological(1,T1)||!Topological(0,T2)){ puts("NO");continue; }int ans=0; for(int i=0;i<T2.size();i++)f[m][i]=0; for(int i=m-1;i>=0;i--){ for(int j=0;j<T2.size();j++)f[i][j]=f[i+1][j]; for(int j=0;j<T2.size();j++){ if(map[T1[i]][T2[j]])f[i][j]=1; } } for(int i=0;i<T2.size();i++){ dp[i]=INT_MIN; int t=m; for(int j=0;j<T1.size();j++){ if(map[T2[i]][T1[j]]){t=j;break;} }if(f[t][i])continue; dp[i]=1; for(int j=0;j<i;j++){ if(!f[t][j])dp[i]=max(dp[i],dp[j]+1); }ans=max(ans,dp[i]); }printf("YES %d\n",ans); }return 0; }
愿你出走半生,归来仍是少年