问题描述:
一块电路板上、下两端分别有n个接线柱,依据电路设计,要求使用导线将(i, a(i)) 将上端接线柱i与下端接线柱a(i)连接,有若干层,但保证第一层中连线的数量最大且不相交,Net = {(i, a(i)) 1 <= i <= n}即最大不相交子集。
书上讲的过于理论化,难以看懂,我按照我的理解分析。
电路布线其实和0-1背包很像,如下图
我想应该看得懂,出现交叉的情况了,为何会出现交叉的情况,本质上红线的下接线柱只能在黑线的左边以保证不相交。
所以红线只能接在左边,我们再看,假设dp(i, j)表示上接线柱 <= i ,下接线柱 <= j的连线的集合,此时遇到(i, a(i)),分两种情况,此时能否纳入呢?
1. j < a[i] 说明不能不能纳入, 否则会产 生相交即 取dp[i - 1][j]
2. j >= a[i] 就就一定可取,不一定,分取与不取:
取则dp[i][j] --> dp[i - 1][a[i ] - 1] + 1;不取即相当于不纳入即 取dp[i - 1][j]。
//取即在前一个(dp[i - 1][a[i] - 1])的基础上 + 1即可
3. 初始化很简单在i == 1时,如果 j < a[1] 则 dp[i][j] = 0,否则置1
源代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
int a[1024], dp[1024][1024];
int main()
{
int i, j, n;
scanf("%d", &n);
for(i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
}
// i == 1时 0 j < a[i](无法布线)
// dp[i][j] = {
// 1 j >= a[i]((i, a[i]) 属于 MNS(i, j))
// i > 1时 dp[i - 1][j] j < a[i]((i, a[i]) 不属于 MNS(i, j), 取前)
// dp[i][j] = {
// max(dp[i - 1][j], dp[i - 1][a[i] - 1] + 1) j >= a[i]((i, a[i]) 属于 MNS(i, j), MNS(i, j) - {(i, a[i])} 为 N(i, a[i])最大不相交子集)
for(j = 0; j < a[1]; ++j) dp[1][j] = 0;
for(j = a[1]; j <= n; ++j) dp[1][j] = 1;
for(i = 2; i <= n; ++i)
{
for(j = 0; j < a[i]; ++j) dp[i][j] = dp[i - 1][j];
for(j = a[i]; j <= n; ++j) dp[i][j] = max(dp[i - 1][j], dp[i - 1][a[i] - 1] + 1);
}
// dp[n][n] = max(dp[n - 1][n], dp[n - 1][a[n] - 1] + 1);
printf("%d\n", dp[n][n]);
return 0;
}
/*
10
8 7 4 2 5 1 9 3 10 6
*/