zoj1108 FatMouse's Speed
给你每个物体两个参数,求最长的链要求第一个参数递增,第二个参数递减,要求输出任意最长路径。
首先第一反应根据第二个参数排个序,然后不就是最长上升子序列的问题吗?
O(nlogn)的复杂度,当然这样可以写,写法也不难。
然后发现这个还是个DAG,也可以用拓扑排序来搞定,输出最长路径,复杂度O(n*n),更新的时候需要更新并记录每个点的前节点,最后倒序输出。
第二种就当练练手吧
先上第二种代码:
#include<cstdio> #include<algorithm> #include<cmath> #include<map> #include<iostream> #include<vector> #include<cstring> #include<queue> using namespace std; int d; struct node { int x,y; }h[1005]; vector<int> t[1005]; int deg[1005]; queue<int> q; int g[1005]; int ans=0; int res=-1; int dp[1005]; vector<int> s; void tp() { for(int i=0;i<d;i++) { if(deg[i]==0) { dp[i]=1; q.push(i); } } while(!q.empty()) { int xx=q.front(); if(dp[xx]>ans) { ans=dp[xx]; res=xx; } q.pop(); for(int i=0;i<t[xx].size();i++) { int w=t[xx][i]; deg[w]--; if(dp[w]<dp[xx]+1) { dp[w]=dp[xx]+1; g[w]=xx; } if(deg[w]==0) q.push(w); } } } int main() { //freopen("input.txt","r",stdin); int a,b; memset(g,-1,sizeof(g)); while(scanf("%d%d",&a,&b)==2) { h[d].x=a; h[d].y=b; d++; } for(int i=0;i<d;i++) for(int j=0;j<d;j++) { if(h[i].x<h[j].x&&h[i].y>h[j].y){ t[i].push_back(j); deg[j]++; } } tp(); printf("%d\n",ans); s.push_back(res+1); while(g[res]!=-1) { s.push_back(g[res]+1); res=g[res]; } int len=s.size(); for(int i=len-1;i>=0;i--) printf("%d\n",s[i]); }
第一种代码稍后补上:
参考http://blog.csdn.net/dangwenliang/article/details/5728363
原理:O(N*N)的,一维,设dp[i]为以第i位为结尾的最长长度,dp[i]=max(dp[j])+1(j<i),然后记录前缀,输出路径。
#include<cstdio> #include<algorithm> #include<cmath> #include<map> #include<iostream> #include<vector> #include<cstring> #include<queue> using namespace std; int d; struct node { int x,y,r; }h[1005]; int hh[1005]; int g[1005]; vector<int> t; bool cmp(node a,node b) { if(a.y>b.y) return 1; return 0; } int f[1005]; int ans,res; int main() { //freopen("input.txt","r",stdin); int a,b; memset(g,-1,sizeof(g)); while(scanf("%d%d",&a,&b)==2) { h[d].x=a; h[d].y=b; h[d].r=d; d++; } sort(h,h+d,cmp); hh[0]=1; int res=-1; ans=0; for(int i=1;i<d;i++) { hh[i]=1; for(int j=0;j<i;j++) { if(h[i].x>h[j].x&&hh[i]<hh[j]+1) { hh[i]=hh[j]+1; g[i]=j; if(hh[i]>res) { ans=i; res=hh[i]; } } } } printf("%d\n",res); t.push_back(ans); while(g[ans]!=-1) { t.push_back(g[ans]); ans=g[ans]; } int len=t.size(); for(int i=len-1;i>=0;i--) { printf("%d\n",h[t[i]].r+1); } }
然后来O(nlogn)算法。
首先能这么做,要满足dp的两个根本原则:1.无后效性,每一个点的状态都由其前面的点决定
2.每一步都是当前那一个整体最优。
我们必须用到二分,但是这个前面的点的序列是不连续的,所以我们需要选出前面的点中每一步中的最优,来组成一个"标杆队列",因为对于数和数之间的前后关系是重要的,但对于当前的数,前面的数的前后关系是不重要的,如果每一次二分,dp的同时维护那个"标杆队列",我们可以保证那个队列是最优的,那么下一个点也会是最优的,也可以保证结果是最优的。
#include<cstdio> #include<algorithm> #include<cmath> #include<map> #include<iostream> #include<vector> #include<cstring> #include<queue> using namespace std; int d=1; struct node { int x,y,r; }h[1005]; int ans[1005]; int hh[1005]; int dp[1005]; bool cmp(node a,node b) { if(a.y>b.y) return 1; else if(a.y==b.y&&a.x>b.x) return 1; return 0; } int main() { //freopen("input.txt","r",stdin); int a,b; while(scanf("%d%d",&a,&b)==2) { h[d].x=a; h[d].y=b; h[d].r=d; d++; } sort(h+1,h+d,cmp); for(int i=1;i<d;i++) ans[i]=10005; int len=0; for(int i=1;i<d;i++) { int t=lower_bound(ans+1,ans+d,h[i].x)-ans; ans[t]=h[i].x; dp[i]=t; len=max(len,t); } printf("%d\n",len); int cc=len; int res=0; int f=10005; for(int i=d-1;i>0;i--) { if(len==0) break; if(dp[i]==len&&h[i].x<f) { hh[res++]=i; len--; f=h[i].x; } } for(int i=res-1;i>=0;i--) { printf("%d\n",h[hh[i]].r); } }