[LG4890]Never·island DP

[LG4890]Never·island DP

~~~题面~~~

题解:

  感到此题非常的神奇。。。看了大佬的题解才懂的。

  首先建模:

  先把所有队伍的出去回来时间都放在一个数组里,然后排序,从左到右扫一边,给每个队伍都建一个带权点,进行如下操作:

  (s表示出发,t表示回家, len表示两个点之间的时间差)  

  1,对于s --- > t的情况,如果两者属于一个队伍,那么给这个队伍加上len,表示如果给这个队伍钥匙可以获得的贡献。

    如果两者不属于同一个队伍,那么在这两个队伍之间连一条边权为len的边,表示如果同时给这2个队伍钥匙可以获得的贡献。

  2,对于s ---> s的情况,给左边的队伍加上len,表示如果给左边队伍钥匙可以获得的贡献。

  3,对于t ---> t的情况,给右边队伍加上len,表示如果给右边队伍钥匙可以获得的贡献。

  4,对于t ---> s的情况,直接给ans += len,因为不需要钥匙就可以获得这个贡献。

  然后考虑如何DP。

  我们观察到每个点最多只有一条入边,最多只有一条出边,因此这些点和边构成了由一堆链组成的图。而这些链两两不相交(不互相干扰)

  因此我们只需要保证在DP时,一条链中的点都按顺序放在一起即可。所以我们对整张图做一个拓扑排序,最后得到的DP顺序应该是类似这样的:(可能有点单独成链)

[LG4890]Never·island DP

  相当于把所有链一一放好,然后DP。

  那么如何转移呢?

  设f[i][j][0/1]表示DP到i位,当前选了j个点,当前点选or不选的贡献。

  那么对于不相连的点,直接更新即可,

  对于相连的点,如果用f[i - 1][j - 1][1]更新f[i][j][1],那么还需要加上两点之间边的边权,表示两个点同时选带来的多余贡献。

  最后的答案等于ans = d[n * 2] - d[1] - ans - max(f[i][j][1], f[i][j][0]);

  d[n * 2] - d[1]是在获取整个线段的长度,减去ans是在减掉一开始就有的贡献(无需钥匙的),max(f[i][j][0], f[i][j][1])是给钥匙带来的贡献,相减后剩下的就是开门的天数。

  (一遍A,感到非常愉悦,而且速度还可以)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define R register int
 4 #define AC 2100
 5 #define ac 5000
 6 #define LL long long 
 7 
 8 int n, k, cnt, ans;
 9 int power[AC], Next[AC], last[AC];
10 int in[AC], d[AC], tot;//存下DP序列
11 LL s[AC], f[AC][AC][2];
12 bool z[AC];
13 //DP到i位,选了j个点,当前点选与不选,s是当前点的向后的边的边权
14 struct node{
15     int x, id;
16     bool z;
17 }p[ac];
18 
19 inline int read()
20 {
21     int x = 0;char c = getchar();
22     while(c > '9' || c < '0') c = getchar();
23     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
24     return x;
25 }
26 
27 inline bool cmp(node a, node b)
28 {
29     return a.x < b.x;
30 }
31 
32 inline void upmax(int &a, int b)
33 {
34     if(b > a) a = b;
35 }
36 
37 void dfs(int x)
38 {
39     d[++tot] = x, z[x] = true;
40     while(Next[x]) d[++tot] = Next[x], x = Next[x], z[x] = true;
41 }
42 
43 void pre()
44 {
45     n = read(), k = read();
46     for(R i = 1; i <= n; i ++)
47     {
48         p[++cnt] = (node){read(), i, 0};
49         p[++cnt] = (node){read(), i, 1};
50     }
51     sort(p + 1, p + cnt + 1, cmp);
52     int b = 2 * n;
53     for(R i = 1; i < b; i ++)
54     {
55         int len = p[i + 1].x - p[i].x, x = p[i].id, y = p[i + 1].id;
56         if(!p[i].z && p[i + 1].z)
57         {
58             if(x == y) power[p[i].id] += len;
59             else Next[x] = y, last[y] = x, s[x] = len, ++ in[y];
60         }    
61         else if(!p[i].z && !p[i + 1].z) power[x] += len;
62         else if(p[i].z && p[i + 1].z) power[y] += len;
63         else ans += len;
64     }
65 }
66 
67 void work()
68 {
69     int b = 2 * n;
70     for(R i = 1; i < b; i ++) //类似于拓扑排序,要没有入度才行
71         if(!z[p[i].id] && !in[p[i].id]) dfs(p[i].id);
72     for(R i = 1; i <= n; i ++)//枚举点
73     {
74         for(R j = 1; j <= k; j ++)
75         {
76             f[i][j][0] = max(f[i - 1][j][0], f[i - 1][j][1]);
77             if(last[d[i]]) f[i][j][1] = max(f[i - 1][j - 1][0], f[i - 1][j - 1][1] + s[last[d[i]]]) + power[d[i]];
78             else f[i][j][1] = max(f[i - 1][j - 1][0], f[i - 1][j - 1][1]) + power[d[i]];
79         }
80     }
81     printf("%lld
", p[n * 2].x - ans - p[1].x - max(f[n][k][1], f[n][k][0]));
82 }
83 
84 int main()
85 {
86     freopen("in.in", "r", stdin);
87     pre();
88     work();
89     fclose(stdin);
90     return 0;
91 }
View Code