【訓練題】DAG中的最長路 P1659
阿新 • • 發佈:2018-11-30
Description
有n個矩形(編號為1~n,每個矩形可以用a,b來描述,表示長和寬。矩形X(a,b)可以巢狀在矩形Y(c,d)中當且僅當a<c,b<d 或者b<c,a<d(相當於X旋轉90度)。例如(1,5)可以巢狀在(6,2)內,但不能巢狀在(3,4)中。你的任務是選出儘可能多的矩形排成一行,使得除最後一個外,每一個矩形都可以巢狀在下一個矩形內。
Input
第1行是一個正正數n,表示該組測試資料中含有矩形的個數。接著輸入a,b。
Output
第1行輸出一個整數,表示最長巢狀矩形序列包含的矩形數目。第2行輸出最長巢狀矩形序列從左到右的矩形編號,如果有多解,輸出字典序最小的。第3行輸出一個整數,表示最長巢狀矩形序列的方案數,這個數可能很大,需要輸出mod10^9+7的結果。
Hint
n<=1000。
Solution
首先toposort的方式是錯的,,因為dp陣列的定義是以這個點為結尾的最長長度,也有可能存在這個點是葉子結點,但因為字典序最小選到它了就不對了。
然後所以正解應該是dfs記憶化搜尋,第一問是記錄長度(從匯聚點n+2開始一次伸展到原點0),第三問也是這個原理。
注意事項: 1.vis,不寫要超時。 2.dfs的情況因為多了原點0和匯聚點n+2所以要減。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define maxn 500005 using namespace std; struct Edge{ int u; int v; int next; }edge[maxn]; int first[maxn],last[maxn],leftt[maxn],rightt[maxn],f[maxn],r[maxn]; int node,n; bool vis[maxn],Vis[maxn]; void addedge(int u,int v){ edge[++node]=(Edge){u,v,0}; if(first[u]==0)first[u]=node; else edge[last[u]].next=node; last[u]=node; } void init(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d",&leftt[i],&rightt[i]); } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(i==j)continue; if((leftt[i]<leftt[j]&&rightt[i]<rightt[j])||(leftt[i]<rightt[j]&&rightt[i]<leftt[j])){ addedge(i,j); } } } for(int i=1;i<=n;i++){ addedge(0,i); addedge(i,n+2); } } int dfs(int s){ if(vis[s])return f[s]; if(!first[s])return f[s]=1; vis[s]=true; for(int i=first[s];i;i=edge[i].next){ int j=edge[i].v; f[s]=max(f[s],dfs(j)+1); } return f[s]; } void outputt(int s){ if(s!=0){ printf("%d ",s); } int goo=n+2; for(int i=first[s];i;i=edge[i].next){ int j=edge[i].v; if(f[j]==f[s]-1)goo=min(goo,j); } if(goo!=n+2)outputt(goo); } int dfs2(int s){ if(!first[s])return r[s]=1; if(Vis[s])return r[s]; Vis[s]=1; for(int i=first[s];i;i=edge[i].next){ int j=edge[i].v; if(f[j]==f[s]-1)r[s]+=dfs2(j),r[s]%=1000000007; } return r[s]; } int main(){ init(); printf("%d\n",dfs(0)-2); outputt(0); printf("\n%d\n",dfs2(0)); return 0; }