看到网上有很多的题解是用链表加上贪心来做的,但是可以去联想“tree【WQS二分+MST】”这道题,可以发现,如果我们对所有的边去删除一个数,我们可以把这道题做成线形的。
我们可以去对所有的边(一共N-1条边)都删去一个数,然后我们可以去想办法去取其中的尽可能多的边,要保证取的边是负数的,然后去看最后的取到的边的个数与K的关系,然后就是一个二分答案的过程了,但是这个二分有点不一样,它有可能取不到真实答案的会直接“”这样类似的
这里就是用到了WQS二分的原因了。
在这里WQS的方案是不唯一的,我在这里的假设就是我们尽可能的取到小的权值(因为减去的是mid(也就是二分答案的数)),然后如果权值相同,我们去取此时放进去的数量更多的。那么趋于答案的正解肯定在这个方向,并且趋于
,那么我们最后放回的正解应该是
return dp[N - 1][0].cnt >= K;
但是正如我说的那样,答案的正解不唯一,WQS的原则就是:前提是可行解,再者要最趋近于正解。
所以按照上述方法推出来的WQS和DP转移方程就是:
include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define eps 1e-9
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e5 + 7;
int N, K, s[maxN];
ll d[maxN];
struct node
{
ll val;
int cnt;
node(ll a=0, int b=0):val(a), cnt(b) {}
friend bool operator < (node e1, node e2) { return e1.val == e2.val ? e1.cnt > e2.cnt : e1.val < e2.val; }
}dp[maxN][2];
inline bool check(ll limit)
{
for(int i=2; i<N; i++) dp[i][0] = dp[i][1] = node(INF, 0);
dp[1][1] = node(d[1] - limit, 1);
dp[1][0] = node();
for(int i=2; i<N; i++)
{
dp[i][0] = min(dp[i-1][1], dp[i-1][0]);
dp[i][1] = node(dp[i - 1][0].val + d[i] - limit, dp[i - 1][0].cnt + 1);
}
dp[N - 1][0] = min(dp[N - 1][0], dp[N - 1][1]);
return dp[N - 1][0].cnt >= K;
}
int main()
{
// freopen("testdata.in", "r", stdin);
scanf("%d%d", &N, &K);
for(int i=1; i<=N; i++) scanf("%d", &s[i]);
for(int i=2; i<=N; i++) d[i - 1] = s[i] - s[i - 1];
ll l = 0, r = s[N], mid = 0;
ll ans = r;
while(l <= r)
{
mid = (l + r) >> 1;
if(check(mid))
{
r = mid - 1;
ans = dp[N - 1][0].val + K * mid;
}
else l = mid + 1;
}
printf("%lld\n", ans);
return 0;
}
这里再给几组样例:
5 2
0 2 5 6 8
6 2
0 1 3 5 7 9
4 1
0 5 10 15
当然,这道题的方法是不唯一的:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define eps 1e-9
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e5 + 7;
int N, K, s[maxN];
ll d[maxN];
struct node
{
ll val;
int cnt;
node(ll a=0, int b=0):val(a), cnt(b) {}
friend bool operator < (node e1, node e2) { return e1.val == e2.val ? e1.cnt < e2.cnt : e1.val < e2.val; }
}dp[maxN][2];
inline bool check(ll limit)
{
for(int i=2; i<N; i++) dp[i][0] = dp[i][1] = node(INF, 0);
dp[1][1] = node(d[1] - limit, 1);
dp[1][0] = node();
for(int i=2; i<N; i++)
{
dp[i][0] = min(dp[i-1][1], dp[i-1][0]);
dp[i][1] = node(dp[i - 1][0].val + d[i] - limit, dp[i - 1][0].cnt + 1);
}
dp[N - 1][0] = min(dp[N - 1][0], dp[N - 1][1]);
return dp[N - 1][0].cnt <= K;
}
int main()
{
// freopen("testdata.in", "r", stdin);
scanf("%d%d", &N, &K);
for(int i=1; i<=N; i++) scanf("%d", &s[i]);
for(int i=2; i<=N; i++) d[i - 1] = s[i] - s[i - 1];
ll l = 0, r = s[N], mid = 0;
ll ans = r;
while(l <= r)
{
mid = (l + r) >> 1;
if(check(mid))
{
l = mid + 1;
ans = dp[N - 1][0].val + K * mid;
}
else r = mid - 1;
}
printf("%lld\n", ans);
return 0;
}