• poj 3666 Making the Grade & zoj 3512 Financial Fraud 左偏树 or dp


    //poj 3666

    //分析:只是在2005年集训队论文黄源河提到的题目上略微有一点点变化

     1 #include"iostream"
     2 #include"cstdio"
     3 using namespace std;
     4 const int maxn = 2100;
     5 int v[maxn],l[maxn],r[maxn],d[maxn];    //节点信息
     6 int N;
     7 int tot,root[maxn],num_now[maxn],num_del[maxn]; //树根信息
     8 
     9 __int64 abs(__int64 ans)
    10 {
    11     return ans<0?-ans:ans;
    12 }
    13 
    14 int Merge(int x,int y)
    15 {
    16     if(!x)  //分治终点
    17         return y;
    18     if(!y)
    19         return x;
    20     if(v[x]<v[y])
    21         swap(x,y);
    22     r[x] = Merge(r[x],y);   //用递归函数进行分治,注意返回值
    23     if(d[l[x]]<d[r[x]]) //回溯,维护数组l,r,d
    24         swap(l[x],r[x]);
    25     d[x] = d[r[x]]+1;
    26     return x;   //返回根节点
    27 }
    28 
    29 __int64 solve()
    30 {
    31     int i,j,k;
    32     __int64 res = 0;
    33     tot = 0;
    34     for(i = 1; i<=N; ++i) { //将每个节点都处理成一个区间(左偏树),根节点保存这个区间的中位数信息
    35         root[++tot] = i;    //树根信息初始化
    36         num_now[tot] = 1;   //树根信息初始化
    37         num_del[tot] = 0;   //树根信息初始化
    38         l[i] = r[i] = d[i] = 0; //节点信息初始化
    39         while(tot>1&&v[root[tot-1]]>v[root[tot]]) { //循环条件为:最后一个区间的中位数小于前一区间;循环槽作为区间合并,并维护树根信息
    40             root[tot-1] =  Merge(root[tot-1],root[tot]);    //注意Merge的返回值
    41             num_now[tot-1] += num_now[tot];
    42             num_del[tot-1] += num_del[tot];
    43             --tot;
    44             while(num_now[tot]>num_del[tot]+1) {
    45                 --num_now[tot];
    46                 ++num_del[tot];
    47                 root[tot] =  Merge(l[root[tot]],r[root[tot]]);  //注意Merge的返回值
    48             }
    49         }
    50     }
    51     for(i = k = 1; i<=tot; ++i) {
    52         for(j = 1; j<=num_now[i]+num_del[i]; ++j,++k) {
    53             res += abs(v[k]-v[root[i]]);
    54         }
    55     }
    56     return res;
    57 }
    58 
    59 int main()
    60 {
    61     int i;
    62     __int64 res_1,res_2;
    63     scanf("%d",&N);
    64     for(i = 1; i<=N; ++i) {
    65         scanf("%d",&v[i]);
    66     }
    67     res_1 = solve();
    68     for(i = 1; i<=N; ++i) {
    69         v[i] *= -1;
    70     }
    71     res_2 = solve();
    72     printf("%I64d
    ",res_1<res_2?res_1:res_2);
    73     return 0;
    74 }

    //zoj 3512 

    //感觉poj 3666数据太弱就去zoj 找了一道看起来一样的题目去交了,不能用__int64 ce一发,没读题wa一发,没注意数据范围re一发,不能交%I64d又wa一发,第五发才ac......

     1 #include"iostream"
     2 #include"cstdio"
     3 using namespace std;
     4 const int maxn = 50100;
     5 int v[maxn],l[maxn],r[maxn],d[maxn];    //节点信息
     6 int N;
     7 int tot,root[maxn],num_now[maxn],num_del[maxn]; //树根信息
     8 
     9 long long abs(long long ans)
    10 {
    11     return ans<0?-ans:ans;
    12 }
    13 
    14 int Merge(int x,int y)
    15 {
    16     if(!x)  //分治终点
    17         return y;
    18     if(!y)
    19         return x;
    20     if(v[x]<v[y])
    21         swap(x,y);
    22     r[x] = Merge(r[x],y);   //用递归函数进行分治,注意返回值
    23     if(d[l[x]]<d[r[x]]) //回溯,维护数组l,r,d
    24         swap(l[x],r[x]);
    25     d[x] = d[r[x]]+1;
    26     return x;   //返回根节点
    27 }
    28 
    29 long long solve()
    30 {
    31     int i,j,k;
    32     long long res = 0;
    33     tot = 0;
    34     for(i = 1; i<=N; ++i) { //将每个节点都处理成一个区间(左偏树),根节点保存这个区间的中位数信息
    35         root[++tot] = i;    //树根信息初始化
    36         num_now[tot] = 1;   //树根信息初始化
    37         num_del[tot] = 0;   //树根信息初始化
    38         l[i] = r[i] = d[i] = 0; //节点信息初始化
    39         while(tot>1&&v[root[tot-1]]>v[root[tot]]) { //循环条件为:最后一个区间的中位数小于前一区间;循环槽作为区间合并,并维护树根信息
    40             root[tot-1] =  Merge(root[tot-1],root[tot]);    //注意Merge的返回值
    41             num_now[tot-1] += num_now[tot];
    42             num_del[tot-1] += num_del[tot];
    43             --tot;
    44             while(num_now[tot]>num_del[tot]+1) {
    45                 --num_now[tot];
    46                 ++num_del[tot];
    47                 root[tot] =  Merge(l[root[tot]],r[root[tot]]);  //注意Merge的返回值
    48             }
    49         }
    50     }
    51     for(i = k = 1; i<=tot; ++i) {
    52         for(j = 1; j<=num_now[i]+num_del[i]; ++j,++k) {
    53             res += abs(v[k]-v[root[i]]);
    54         }
    55     }
    56     return res;
    57 }
    58 
    59 int main()
    60 {
    61     int i;
    62     long long res_1;
    63     while(scanf("%d",&N)&&N) {
    64         for(i = 1; i<=N; ++i) {
    65             scanf("%d",&v[i]);
    66         }
    67         res_1 = solve();
    68         printf("%lld
    ",res_1);
    69     }
    70     return 0;
    71 }

    //poj 3666,34696K内存过的,以后第一维状态还是压缩一下好了...

     1 #include"iostream"
     2 #include"cstdio"
     3 #include"cstring"
     4 #include"algorithm"
     5 #include"cmath"
     6 using namespace std;
     7 const int maxn = 2100;
     8 __int64 dp[maxn][maxn]; //第一维状态:数组 a 下标,可状压;第二维状态:排序后的数组 b 前缀集合的大小
     9 int n,a[maxn],b[maxn];  //左偏树的做法是不断的取数组 a 单个元素构成的区间的中位数作为 数组 b 中的元素,若数组 b 开始下降,就从后往前做一趟区间合并
    10 //而 dp 的做法则更加粗犷,因为对于 a[i] ,dp显然要枚举出所有可能做它“中位数”的 b[j],所以 dp 不关注选谁作为“中位数”,只关注枚举的顺序。以递增的顺序枚举,所得到的最优解的“中位数”在平面直角坐标系上就是一条不降的曲线
    11 //第一维状态是类前缀集合的大小,第二维状态是前缀集合的大小,所以很容易得到状态转移方程为 dp[i][j] = min(dp[i-1][j]+cost,dp[i][j-1]);
    12 int main()
    13 {
    14     int i,j;
    15     scanf("%d",&n);
    16     memset(dp,0x3f,sizeof(dp));
    17     memset(dp[0],0,sizeof(dp[0]));  //因为第二维状态为前缀集合的大小,所以当 i==0 时整个第二维状态全部都需要初始化
    18     for(i = 1; i<=n; ++i) {
    19         scanf("%d",&a[i]);
    20         b[i] = a[i];
    21     }
    22     sort(b+1,b+1+n);
    23     for(i = 1; i<=n; ++i) {
    24         for(j = 1; j<=n; ++j) {
    25             dp[i][j] = min(dp[i][j-1],dp[i-1][j]+abs(a[i]-b[j]));
    26         }
    27     }
    28     printf("%I64d
    ",dp[n][n]);
    29     return 0;
    30 }

    //poj 3666,状态压缩 dp 176K内存过

     1 #include"iostream"
     2 #include"cstdio"
     3 #include"cstring"
     4 #include"algorithm"
     5 using namespace std;
     6 const int maxn = 2010;
     7 __int64 dp[2][maxn];
     8 int n,a[maxn],b[maxn];
     9 
    10 int main()
    11 {
    12     int i,j;
    13     scanf("%d",&n);
    14     for(i = 1; i<=n; ++i) {
    15         scanf("%d",&a[i]);
    16         b[i] = a[i];
    17     }
    18     sort(b+1,b+1+n);
    19     bool now = 0;
    20     for(i = 1; i<=n; ++i) {
    21         now ^= 1;
    22         memset(dp[now],0x3f,sizeof(dp[now]));
    23         for(j = 1; j<=n; ++j) {
    24             dp[now][j] = min(dp[now^1][j]+abs(a[i]-b[j]),dp[now][j-1]);
    25         }
    26     }
    27     printf("%I64d
    ",dp[now][n]);
    28     return 0;
    29 }
  • 相关阅读:
    unity的旋转
    Unity自带寻路Navmesh入门教程(三)
    Unity自带寻路Navmesh入门教程(二)
    unity自带寻路Navmesh入门教程(一)
    分隔字符串,计算一个字符串内数字个数、汉字个数、字母个数
    在 iTerm2 终端使用 command + ;会弹出最近使用的命令列表
    mac 电脑设置密码可以直接使用 passwd 这个命令
    Python 正则表达式
    设计模式学习 —— 模板方法
    Spring Boot 中使用 spring-boot-devtools (使用 Gradle 作为构建工具)
  • 原文地址:https://www.cnblogs.com/AC-Phoenix/p/4268756.html
Copyright © 2020-2023  润新知