AtCoder AGC036D Negative Cycle (图论、DP)
題目鏈接
https://atcoder.jp/contests/agc036/tasks/agc036_d
題解
這都是怎么想出來的啊。。目瞪口呆系列。。
第一步轉化至關重要: 一張圖中不存在負環意味著什么?
不存在負環就存在最短路,我們可以給每個點分配一個權值\(p_i\)(相當于從\(1\)號到該點的最短路,點從\(1\)開始標號)滿足對于任何邊\((i,j)\)有\(p_j\ge p_i+w(i,j)\).
然后我們令\(q_i=p_i-p_{i+1}\), 那么由于邊權都是\(1\)或者\(-1\)并且存在不能刪的\(0\)邊, 顯然有\(q\)數組的值都是\(0\)或者\(1\).
約束變成了: 對于每條邊\((i,j)\ (i>j)\)有\(\sum^{i-1}_{k=i}q_k\le 1\), 對于每條邊\((i,j)\ (i<j)\)有\(\sum^{j-1}_{k=i}q_k\ge 1\).
所以問題就被轉化成了: 你要給每個\(1\)到\((n-1)\)中的點\(q_i\)分配一個\(0\)或者\(1\)的權值,再刪掉所有不滿足約束條件的邊,使得總代價最小!
天哪,這也太神仙了吧……
然后就是一個很容易的DP了,設\(dp[i][j]\)表示安排好前\(i\)位的\(q\)值,且強行令\(q_i=1\), 上一個為\(1\)的位置是\(j\)
那么考慮枚舉\(k\), \(dp[i][j]\)轉移到\(dp[k][i]\),同時刪去不合法的邊
對于\(a>b\)的邊\((a,b)\), 要刪掉所有滿足\(j<b\le i<x<a\)的邊
對于\(a<b\)的邊\((a,b)\), 要刪掉所有滿足\(j<i<a<b\le x\)的邊
然后這個很容易使用二維前綴和優化,時間復雜度\(O(n^3)\).
啊啊啊人類智慧……
代碼
#include<cstdio> #include<cstdlib> #include<cstring> #include<cassert> #include<iostream> #define llong long long using namespace std;inline int read() {int x=0; bool f=1; char c=getchar();for(;!isdigit(c);c=getchar()) if(c=='-') f=0;for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');if(f) return x;return -x; }const int N = 500; llong a[N+3][N+3]; llong s[2][N+3][N+3]; llong dp[N+3][N+3]; int n;void update(llong &x,llong y) {x = x<y?x:y;}llong getsum(int typ,int lx,int rx,int ly,int ry) {return s[typ][rx][ry]-s[typ][lx-1][ry]-s[typ][rx][ly-1]+s[typ][lx-1][ly-1]; }int main() {scanf("%d",&n);for(int i=1; i<=n; i++){for(int j=1; j<=n; j++){if(j==i) continue;scanf("%lld",&a[i][j]);}}for(int i=1; i<=n; i++){for(int j=1; j<=n; j++){if(i<j) {s[0][i][j] = a[i][j];}s[0][i][j] += s[0][i][j-1];}for(int j=1; j<=n; j++) s[0][i][j] += s[0][i-1][j];}for(int i=1; i<=n; i++){for(int j=1; j<=n; j++){if(i>j) {s[1][i][j] = a[i][j];}s[1][i][j] += s[1][i][j-1];}for(int j=1; j<=n; j++) s[1][i][j] += s[1][i-1][j];}memset(dp,42,sizeof(dp)); dp[0][0] = 0ll;for(int i=0; i<=n; i++){for(int j=0; j<max(i,1); j++){for(int k=i+1; k<=n; k++){llong tmp = dp[i][j]+getsum(1,k+1,n,j+1,i)+getsum(0,i+1,k,i+1,k);update(dp[k][i],tmp);}}}llong ans = dp[n][1];for(int i=1; i<=n; i++) update(ans,dp[n][i]);printf("%lld\n",ans);return 0; }總結
以上是生活随笔為你收集整理的AtCoder AGC036D Negative Cycle (图论、DP)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AtCoder AGC036C GP 2
- 下一篇: AtCoder AGC032D Rota