生活随笔
收集整理的這篇文章主要介紹了
连锁商店 状态压缩dp(女赛)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題意 :
- 每個點都屬于一家公司,每個點都對應一個權值。對于一條路徑,屬于同一家公司的一些點的貢獻只能被算一次。給一張圖,路徑只能從小往大走,問從1走到每個點路徑上分別的最大權值和
思路 :
- n最大為36,說明出現多個點的公司最大為n2\frac{n}{2}2n?,不難發現,對于同一條路徑,如果這條路徑上有些點所屬公司只有這個點,那么必然直接選上這個點,對于只出現過一次的商店不需要存入狀態
- 出現狀態分裂的狀況為出現多個點那些公司,這些才存入狀態,那么時間復雜度是O(2n2?n2)O(2^{\frac{n}{2}}*n^{2})O(22n??n2),如果不優化的話就是O(2n?n2)O(2^{n}*n^2)O(2n?n2)會T
- 可以用二進制壓縮出現多個點的公司的狀態,dp[i][j]dp[i][j]dp[i][j]表示當前在i點,出現多個點的公司狀態為j
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <unordered_map>
#define fi first
#define se second
#define pb push_backusing namespace std
;typedef long long ll
;const int N
= 40, M
= 1 << 20;int n
, m
;
int c
[N
], w
[N
];
int dp
[40][M
];
vector
<int> edge
[N
];
unordered_map
<int, int> mp
;
vector
<int> ve
;void solve()
{scanf("%d%d", &n
, &m
);for (int i
= 1; i
<= n
; i
++ ){scanf("%d", &c
[i
]);mp
[c
[i
]] ++ ;}for (int i
= 1; i
<= n
; i
++ ) scanf("%d", &w
[i
]);for (auto w
: mp
)if (w
.se
> 1)ve
.pb(w
.fi
);for (int i
= 1, u
, v
; i
<= m
; i
++ ){scanf("%d%d", &u
, &v
);edge
[v
].pb(u
); }edge
[1].pb(0);for (int i
= 1; i
<= n
; i
++ ){int id
= -1; for (int j
= 0; j
< (int)ve
.size(); j
++ )if (c
[i
] == ve
[j
]){id
= j
;break;}if (id
== -1) {for (int j
= 0; j
< (1 << (int)ve
.size()); j
++ )for (auto from
: edge
[i
])dp
[i
][j
] = max(dp
[i
][j
], dp
[from
][j
] + w
[c
[i
]]);}else {for (int j
= 0; j
< (1 << (int)ve
.size()); j
++ ) {if (j
& (1 << id
)) {for (auto from
: edge
[i
])dp
[i
][j
] = max(dp
[i
][j
], dp
[from
][j
^ (1 << id
)] + w
[c
[i
]]); }else {for (auto from
: edge
[i
])dp
[i
][j
] = max(dp
[i
][j
], dp
[from
][j
]);}}}int ans
= 0;for (int j
= 0; j
< (1 << (int)ve
.size()); j
++ ) ans
= max(ans
, dp
[i
][j
]);cout
<< ans
<< endl
;}
}int main()
{
int _
= 1;
while (_
-- ){solve();}return 0;
}
總結
以上是生活随笔為你收集整理的连锁商店 状态压缩dp(女赛)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。