zoukankan      html  css  js  c++  java
  • 洛谷P2672 推销员

    沙雕贪心......

    我一开始想的是倒着来,每次减去一个。

    然后我们就有两个决策:去掉最后一个/去掉前面某一个。

    然后第一个决策用并查集维护,第二个决策用线段树即可。仔细想想觉得普及组不会考这种东西,慌得一批。

    然后又发现可能有问题:你可能取x个的时候不从x + 1转移过来,而是x + 2

    然后就不会了。

    然后看提解发现正解是顺着来......什么沙雕。

    结论:若取x个的时候最优解是集合S,那么取x+1个时的最优解集合一定包含S。(说明了上面我的做法是对的)

    证:

    即证对于每一个取x+1的方案p,若不包含S,都可以找到一个包含S的方案比它更优。

    设取x个的最优方案为r

    考虑最右那一个:

    ①p的最后那个等于r的最右那一个时,前面我们随便去掉一个不与r配对的位置d,然后p一定还与r不同。

    我们把p调整成r,然后加上d,这样就比原来的p更优了。

    ②p的最后那个小于r的最后那一个时,我们同样去掉一个d,然后调整,最后加上d,就会更优。

    ③p的最后那个大于r的最后那个时,把p的最后那个去掉,同时p的价值减去(2 * 从r最后到p最后的距离)。

    这样就相当于情况①中去掉d之后的p了。

    然后调整成r之后把原来p的最后加上,再加上减去的价值,就会比原来的p更优。

     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <queue>
     4 
     5 const int N = 100010;
     6 
     7 int a[N], x[N], g[N];
     8 std::priority_queue<int> Q;
     9 
    10 int main() {
    11     int n;
    12     scanf("%d", &n);
    13     for(int i = 1; i <= n; i++) {
    14         scanf("%d", &x[i]);
    15         x[i] <<= 1;
    16     }
    17     for(int i = 1; i <= n; i++) {
    18         scanf("%d", &a[i]);
    19     }
    20     
    21     for(int i = n; i >= 1; i--) {
    22         if(x[i] + a[i] > x[g[i + 1]] + a[g[i + 1]]) {
    23             g[i] = i;
    24         }
    25         else {
    26             g[i] = g[i + 1];
    27         }
    28     }
    29     
    30     int now = g[1];
    31     
    32     int ans = a[now] + x[now], pos = 1;
    33     printf("%d
    ", ans);
    34     
    35     for(int i = 2; i <= n; i++) {
    36         while(pos < now) {
    37             Q.push(a[pos]);
    38             pos++;
    39         }
    40         if(pos == now) {
    41             pos++;
    42         }
    43         if(!Q.empty() && Q.top() > a[g[now + 1]] + x[g[now + 1]] - x[now]) {
    44             ans += Q.top();
    45             Q.pop();
    46         }
    47         else {
    48             ans -= x[now];
    49             now = g[now + 1];
    50             ans += a[now] + x[now];
    51         }
    52         printf("%d
    ", ans);
    53     }
    54     
    55     return 0;
    56 }
    AC代码

    然后我又打了一开始那个线段树的想法......

      1 #include <cstdio>
      2 #include <algorithm>
      3 #include <queue>
      4 
      5 const int N = 100010, INF = 0x3f3f3f3f;
      6 
      7 int a[N], x[N], ans[N];
      8 int small[N << 2], fa[N]; 
      9 
     10 int find(int x) {
     11     if(fa[x] == x) {
     12         return x;
     13     }
     14     return fa[x] = find(fa[x]);
     15 }
     16 
     17 inline void pushup(int o) {
     18     int ls = o << 1;
     19     int rs = ls | 1;
     20     if(a[small[ls]] <= a[small[rs]]) {
     21         small[o] = small[ls];
     22     }
     23     else {
     24         small[o] = small[rs];
     25     }
     26     return;
     27 }
     28 
     29 void build(int l, int r, int o) {
     30     if(l == r) {
     31         small[o] = r;
     32         return;
     33     }
     34     int mid = (l + r) >> 1;
     35     build(l, mid, o << 1);
     36     build(mid + 1, r, o << 1 | 1);
     37     pushup(o);
     38     return;
     39 }
     40 
     41 int ask(int L, int R, int l, int r, int o) {
     42     if(L <= l && r <= R) {
     43         return small[o];
     44     }
     45     int mid = (l + r) >> 1;
     46     
     47     if(R <= mid) {
     48         return ask(L, R, l, mid, o << 1);
     49     }
     50     if(mid < L) {
     51         return ask(L, R, mid + 1, r, o << 1 | 1);
     52     }
     53     
     54     int as = ask(L, R, l, mid, o << 1);
     55     int t = ask(L, R, mid + 1, r, o << 1 | 1);
     56     if(a[t] < a[as]) {
     57         as = t;
     58     }
     59     return as;
     60 }
     61 
     62 void change(int p, int l, int r, int o) {
     63     if(l == r) {
     64         a[r] = INF;
     65         return;
     66     }
     67     int mid = (l + r) >> 1;
     68     if(p <= mid) {
     69         change(p, l, mid, o << 1);
     70     }
     71     else {
     72         change(p, mid + 1, r, o << 1 | 1);
     73     }
     74     pushup(o);
     75     return;
     76 }
     77 
     78 int main() {
     79     int n, sum = 0;
     80     scanf("%d", &n);
     81     for(int i = 1; i <= n; i++) {
     82         scanf("%d", &x[i]);
     83         x[i] <<= 1;
     84     }
     85     for(int i = 1; i <= n; i++) {
     86         scanf("%d", &a[i]);
     87         fa[i] = i;
     88         sum += a[i];
     89     }
     90     sum += x[n];
     91     ans[n] = sum;
     92     
     93     build(1, n, 1);
     94     
     95     int now = n;
     96     for(int i = n - 1; i >= 1; i--) {
     97         int pos = ask(1, now - 1, 1, n, 1);
     98         if(a[now] + x[now] - x[find(now - 1)] > a[pos]) {
     99             sum -= a[pos];
    100             change(pos, 1, n, 1);
    101             fa[pos] = find(pos - 1);
    102         }
    103         else {
    104             sum -= (a[now] + x[now] - x[find(now - 1)]);
    105             now = find(now - 1);
    106         }
    107         ans[i] = sum;
    108     }
    109     
    110     for(int i = 1; i <= n; i++) {
    111         printf("%d
    ", ans[i]);
    112     }
    113     return 0;
    114 }
    AC代码

    话说这个代码我调都没调,一次就写对了。

  • 相关阅读:
    Java 编程下的并发线程之间的同步代码块死锁
    Java 编程下的同步代码块
    Android 编程下两种方式注册广播的区别
    Java 编程下泛型的内部原理
    Android 编程下使用 Google 的 Gson 解析 Json
    c# 调用音库开发英语单词记忆本
    在.Net中实现RichClient+Restful+JPA架构探索实现
    Extjs 常用正则式
    企业内IT部门的一些问题总结
    WinServer2003部署VS2010的水晶报表
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/9814846.html
Copyright © 2011-2022 走看看