日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【学习笔记】有向无环图上的DP

發布時間:2025/3/15 编程问答 9 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【学习笔记】有向无环图上的DP 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【學習筆記】有向無環圖上的DP

手動博客搬家: 本文發表于20180716 10:49:04, 原地址https://blog.csdn.net/suncongbo/article/details/81061378

首先,感謝以下幾位大佬們在此問題上對我的幫助:本市大佬sdqd01, 外省大佬ez_dc, coconight, szlhx01, jxgz03 (均為某OJ用戶名)

一、基本概念

有向無環圖 (Directed Acyclic Graph, DAG): 沒有環的有向圖。
Tarjan算法縮點、拓撲排序
在有向無環圖上,可以進行動態規劃來求解問題,具體見后面的例題。

二、問題引入

一切都要從半年前說起:
半年前我正在準備地理生物中考,其中生物有這樣一種題:
給一個食物網(當然是DAG啦),求該食物網里一共有幾條食物鏈。
當時同學們的做法是:枚舉每一條食物鏈。
先不考慮是否符合生物學原理,最壞情況下的復雜度?
\(O(n2^{n-2})\) (把邊全連滿)

但是我們可以\(DP\)
我的做法:令\(dp[i]\)表示以\(i\)結束(或稱為在\(i\)處匯聚)的食物鏈條數。(食物鏈都是從被捕食者向捕食者連有向邊)
則轉移為:\(dp[i]=\sum_{j\in ind[i]}dp[j]\), 其中\(ind[i]\)為連向\(i\)的點的集合。
初始狀態:\(dp[生產者]=1\), 生產者即入度為0的點。
答案:\(ans=\sum dp[最高級消費者]\),最高級消費者即出度為0的點。
但是一個問題是:如果我們用代碼實現這個過程,如何確定dp的順序?
很顯然,一個點的\(dp\)值能夠被確定,其先決條件是它的所有入點的\(dp\)值都已被確定。因此我們需要確定一個點的排列,使得每個點的所有入點都在這個點之前出現。這里用到拓撲排序。拓撲序就是我們\(DP\)的順序。
那這樣的話,先拓撲排序,記下來拓撲序列,然后從前往后DP?
其實還不用。我們可以直接一邊拓撲排序一邊DP,就是每\(BFS\)訪問到一個節點\(i\),我們枚舉\(i\)的所有出度,對于\(i\)的一個出度\(j\), 刪掉\(i\)\(j\)的邊 (拓撲排序)同時用\(dp[i]\)更新\(dp[j]\) (DP).
一邊拓撲排序,一邊dp,既省空間又省代碼。
復雜度?\(n\)個點\(m\)條邊的話,\(O(n+m)\).
現在,假定這個圖一定是DAG. 對于不是DAG的情況,將在后面討論。

三、例題
  • codeforces 919D (http://codeforces.com/problemset/problem/919/D)
    https://blog.csdn.net/suncongbo/article/details/81061500

  • bzoj 1924 (https://www.lydsy.com/JudgeOnline/problem.php?id=1924)
    首先,這道題難點在建圖,這個過程不是本文的重點,不再贅述。
    假設我們已經建出了圖。現在我們需要求圖上的最長鏈。有以下三種方法:
  • (1) SPFA跑最長路。 (2) DP. 有BFS和DFS兩種方法。
    DP做法(BFS):令\(dp[i]\)表示到i為止最長鏈的長度,則有\(dp[i]=\max_{j\in ind[i]} dp[j]+1\),按拓撲序轉移即可。
    等等?建出的圖上可能有環?
    此時答案顯然不能是\(\inf\), 因為在本題中走過一個點多次只會統計一次答案。所以我們必須通過Tarjan算法來縮點,將每個強連通分量縮成一個點,點的權值為強連通分量的大小。這樣的話,如果題目里的Henry沿最長鏈走到了這個強連通分量中的一個點,則此時如果順道把這個強連通分量訪問遍,答案顯然不會更差。縮完點之后,變成了\(DAG\),然后就可以\(DP\)啦。
    但是!除了BFS,我們還有一種更簡單的\(DP\)方法,即\(DFS\)!時間復雜度一樣,BFS20行左右,DFS不到10行,而且省一點空間
    具體做法:令\(dp[i]\)為從i 開始的最長鏈。代碼如下:

    void dfs(int u,int prv) {if(dp[u]-a[u]>0) return;dp[u] = a[u];for(int i=fe0[u]; i; i=e0[i].nxt){dfs(e0[i].v,u);dp[u] = max(dp[u],dp[e0[i].v]+a[u]);} }

    是不是特別簡潔!不需要冗長的拓撲排序!
    但是它是如何保證轉移順序的呢?
    由于是從i開始,因此轉移順序應是每個點的所有出邊連向的點轉移后才轉移這個點,而代碼中的for循環恰恰保證了這一點。
    整道題代碼:(dfs)

    #include<cstdio> #include<vector> #include<algorithm> using namespace std;const int N = 300000; const int M = 1200000; const int C = 1000000; struct Edge {int v,nxt; bool us; } e[M+2],e0[M+2]; struct Node {int x,y,z;bool operator <(const Node &arg) const{return y<arg.y;} } nd[N+2]; int fe[N+2],fe0[N+2]; int f[3][C+2]; int id1[N+2],id2[N+2]; int dfn[N+2],low[N+2]; int sta[N+2]; bool ins[N+2]; int clr[N+2]; int a[N+2]; int ind[N+2]; int que[N+2]; int dp[N+2]; vector<int> v1,v2; int n,m,m0,num,cnt,tp,r,c;void addedge(int u,int v) {m++; e[m].v = v;e[m].nxt = fe[u]; fe[u] = m; }void addedge0(int u,int v) {m0++; e0[m0].v = v;e0[m0].nxt = fe0[u]; fe0[u] = m0;ind[v]++; }int getid1(int x) {return lower_bound(v1.begin(),v1.end(),x)-v1.begin()+1; }int getid2(int x) {return lower_bound(v2.begin(),v2.end(),x)-v2.begin()+1; }void Tarjan(int u) {cnt++; dfn[u] = cnt; low[u] = cnt; ins[u] = true;tp++; sta[tp] = u;for(int i=fe[u]; i; i=e[i].nxt){if(dfn[e[i].v]==0) {Tarjan(e[i].v); low[u] = min(low[u],low[e[i].v]);}else if(ins[e[i].v]) {low[u] = min(low[u],dfn[e[i].v]);}}if(low[u]==dfn[u]){num++; a[num] = 1;while(sta[tp]!=u){ins[sta[tp]] = false;clr[sta[tp]] = num;a[num]++;tp--;}ins[u] = false; clr[u] = num; tp--;} }void dfs(int u,int prv) {if(dp[u]-a[u]>0) return;dp[u] = a[u];for(int i=fe0[u]; i; i=e0[i].nxt){if(e0[i].v==prv) continue;dfs(e0[i].v,u);dp[u] = max(dp[u],dp[e0[i].v]+a[u]);} }int main() {scanf("%d%d%d",&n,&r,&c); m = 0;for(int i=1; i<=n; i++){scanf("%d%d%d",&nd[i].x,&nd[i].y,&nd[i].z);v1.push_back(nd[i].x); v2.push_back(nd[i].y);}sort(nd+1,nd+n+1);int cur = 0,nxt = 1,prv = -1,pcur = 1,pnxt = 1,pprv = 0;while(nd[pnxt].y==1) {f[nxt][nd[pnxt].x] = pnxt; pnxt++;}for(int i=1; i<=c; i++){cur = (cur+1)%3; nxt = (nxt+1)%3; prv = (prv+1)%3;while(pnxt<=n && nd[pnxt].y==i+1){f[nxt][nd[pnxt].x] = pnxt;pnxt++;}while(nd[pcur].y==i){if(nd[pcur].z==3){if(f[prv][nd[pcur].x]>0) addedge(pcur,f[prv][nd[pcur].x]);if(f[prv][nd[pcur].x-1]>0) addedge(pcur,f[prv][nd[pcur].x-1]);if(f[prv][nd[pcur].x+1]>0) addedge(pcur,f[prv][nd[pcur].x+1]);if(f[cur][nd[pcur].x-1]>0) addedge(pcur,f[cur][nd[pcur].x-1]);if(f[cur][nd[pcur].x+1]>0) addedge(pcur,f[cur][nd[pcur].x+1]);if(f[nxt][nd[pcur].x]>0) addedge(pcur,f[nxt][nd[pcur].x]);if(f[nxt][nd[pcur].x-1]>0) addedge(pcur,f[nxt][nd[pcur].x-1]);if(f[nxt][nd[pcur].x+1]>0) addedge(pcur,f[nxt][nd[pcur].x+1]);}pcur++;}while(nd[pprv].y==i-1){f[prv][nd[pprv].x] = 0;pprv++;}}sort(v1.begin(),v1.end()); v1.erase(unique(v1.begin(),v1.end()),v1.end());sort(v2.begin(),v2.end()); v2.erase(unique(v2.begin(),v2.end()),v2.end());for(int i=1; i<=n; i++){nd[i].x = getid1(nd[i].x); nd[i].y = getid2(nd[i].y);if(nd[i].z==1) id1[nd[i].x] = i; else if(nd[i].z==2) id2[nd[i].y] = i;}for(int i=1; i<=n; i++){if(id1[nd[i].x]!=i) addedge(id1[nd[i].x],i);if(id2[nd[i].y]!=i) addedge(id2[nd[i].y],i);if(nd[i].z==1 && id1[nd[i].x]!=i) addedge(i,id1[nd[i].x]);else if(nd[i].z==2 && id2[nd[i].y]!=i) addedge(i,id2[nd[i].y]);}cnt = 0; num = 0; for(int i=1; i<=n; i++) if(dfn[i]==0) Tarjan(i);m0 = 0;for(int i=1; i<=n; i++){for(int j=fe[i]; j; j=e[j].nxt){if(clr[i]!=clr[e[j].v]){addedge0(clr[i],clr[e[j].v]);}}}int ans = 0;for(int i=1; i<=num; i++) if(ind[i]==0) {dfs(i,0); ans = max(ans,dp[i]);}printf("%d\n",ans);return 0; }
  • bzoj 1179 (https://www.lydsy.com/JudgeOnline/problem.php?id=1179)
    https://blog.csdn.net/suncongbo/article/details/81061601
  • 發表于 2019-01-16 13:01 suncongbo 閱讀(...) 評論(...) 編輯 收藏 刷新評論刷新頁面返回頂部

    總結

    以上是生活随笔為你收集整理的【学习笔记】有向无环图上的DP的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 国产精品99久久久久久久久久久久 | 九九热精品在线视频 | 久久久久草| 亚洲综合免费视频 | 久色视频| 日本a在线 | 伊人久久久久噜噜噜亚洲熟女综合 | 精品一区二区人妻 | 日韩丝袜一区 | 亚洲天天综合 | 久久国产亚洲 | 综合视频在线观看 | 中文字幕在线资源 | 手机av在线免费观看 | 精品一区精品二区 | 成人免费版欧美州 | 青娱乐最新地址 | 国语对白永久免费 | 亚洲乱码一区二区 | www毛片com | 欧洲一区二区三区在线 | 中文亚洲欧美 | 国产一级特黄毛片 | 福利视频不卡 | 国产精品久久久久久久无码 | 法国伦理少妇愉情 | 欧美黑人添添高潮a片www | 亚洲av无码国产精品色午夜 | 精品成人av| 亚洲一线av| 深夜免费视频 | 天天爱夜夜爱 | 日本黄页网址 | 久草视频免费在线 | av免费影院 | 色综合中文 | 尤物在线视频观看 | 网站色 | 亚洲看看 | 精品人妻中文无码av在线 | 亚洲黄网站在线观看 | av色欲无码人妻中文字幕 | 色综合视频在线观看 | 欧美大片在线免费观看 | jizz精品| 男女羞羞动态图 | 国产精品丝袜黑色高跟 | 日本欧美久久久久免费播放网 | 黄页网址大全免费观看 | 国产成人精品免高潮费视频 | 色综合av综合无码综合网站 | 国产又大又黄又粗 | 日本一级淫片色费放 | 欧美午夜一区二区 | 欧美a视频 | 亚洲精品天天 | 潘金莲一级淫片aaaaaa播放 | 超碰在线国产 | 91精品国产综合久久福利软件 | 久久精品美女 | 亚洲午夜无码久久 | 久草资源站 | 久久夜色精品国产欧美乱极品 | 51精产品一区一区三区 | 可以免费看毛片的网站 | 草草久久久无码国产专区 | 成人黄色大片在线观看 | 欧美性一区 | 久久久久久久久91 | 天天操夜夜操夜夜操 | 午夜精品一区二 | 欧美sese | 蜜臀av性久久久久蜜臀aⅴ麻豆 | 91福利免费视频 | 日韩在线成人 | 亚州av成人| 成人免费播放视频 | 亚洲码中文 | 日本一本久久 | 日本美女动态 | 免费黄网站在线看 | 亚洲裸体视频 | 激情图片区 | 日韩精品一区二区在线看 | 尤物在线精品 | 日韩精品一区二区三 | 国产人妻人伦精品1国产盗摄 | a猛片| 国产精品一区麻豆 | 丰满人妻熟女aⅴ一区 | 国产一区在线播放 | 欧美精品一区二区性色a+v | 九一网站在线观看 | 古代黄色片 | 精品人妻久久久久久888不卡 | 不许穿内裤随时挨c调教h苏绵 | 魔女鞋交玉足榨精调教 | 久草免费在线视频 | 亚洲男人精品 |