题意 :
- n+1个点的无向图,点编号0-n,已知 i ( 1 ≤ i ≤ n ) i(1 \leq i \leq n) i(1≤i≤n)与0之间边权值为 a i a_i ai, i i i与 i + 1 i + 1 i+1之间边权值为 b i b_i bi( n n n与 1 1 1之间也有边)
- 现在要删去一些边使图变成二分图(bipatite),问删去的边的权值和最小为多少
思路 :
- 删边变成二分图,用同一种颜色代表二分图同一集合中的点,将所有点染色(两种颜色),那么需要满足同一种颜色的点之间没有边,因此删去的是同一种颜色的点之间的边
- 题意转换为 将0-n点染成红色或蓝色,使
相同颜色的点之间的边权和最小
,且答案就是最小的相同颜色的点之间的边权和 - 不失普遍性地,假设0点是红色,也就是颜色为0
- 要计算相同颜色的点之间的边权和,显然对于每个点来说,与它本身颜色和0号点颜色以及它本身颜色和上一个点颜色有关
- d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示当前从1号点转移到i号点,1号点的颜色是k,i号点的颜色是j的最小的同种颜色的边权和,注意这里不考虑1号点与n号点之间的边
- 之所以状态中是“1号点的颜色”,是因为如果改为上一个点的颜色,状态转移本质是一样的;这样的话状态转移中再加一维“上一个点的颜色”就方便转移了
- 状态转移,四重循环,i号点,i号点的颜色j,
1号点
的颜色k,上一个点
的颜色prej,如果i号点的颜色是0,加上与0点的边,如果i号点颜色和i-1号点颜色一致,加上它们之间的边 - 最后循环更新答案时,还要考虑是否加上n号点(当前点)与1号点之间的边
- 时间复杂度是 O ( N ) O(N) O(N)
- chmin函数,两个变量,第一个变量变成两者中最小值
- 由于求的是最小,所以初始化为inf,注意dp数组的赋值初始化方法,三维,先照抄最外面的vector里面的东西,然后它再写一对圆括号,圆括号里先写上这维的大小是2,然后是再照抄着一层的vector里面的内容,同样圆括号,这一维大小是2,元素都是inf
- 注意这题要开ll,所以干脆所有变量都ll
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <unordered_set>
#include <math.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
#define endl '\n'
#define fi first
#define se second
#define push_back
#define rep(i, l, r) for (ll i = l; i <= r; i ++ )
const ll inf = 1e18;
void chinmin(ll &a, ll b)
{
a = min(a, b);
}
int main()
{
cin.tie(nullptr) -> sync_with_stdio(false);
ll n; cin >> n;
vector<vector<vector<ll>>> dp(n + 1, vector<vector<ll>>(2, vector<ll>(2, inf)));
vector<ll> a(n + 1);
vector<ll> b(n + 1);
rep(i, 1, n) cin >> a[i];
rep(i, 1, n) cin >> b[i];
dp[1][0][0] = a[1];
dp[1][1][1] = 0;
rep(i, 2, n) rep(j, 0, 1) rep(k, 0, 1) rep(prej, 0, 1)
{
chinmin(dp[i][j][k], dp[i - 1][prej][k] + (j == 0 ? a[i] : 0) + (j == prej ? b[i - 1] : 0));
}
ll ans = inf;
rep(j, 0, 1) rep(k, 0, 1)
{
chinmin(ans, dp[n][j][k] + (j == k ? b[n] : 0));
}
cout << ans << endl;
return 0;
}