hdu 4666 (经典求曼哈顿距离 + multiset容器)

Sample Input
  
  
10 2 0 208 403 0 371 -180 1 2 0 1069 -192 0 418 -525 1 5 1 1 0 2754 635 0 -2491 961 0 2954 -2516
 

Sample Output
  
  
0 746 0 1456 1456 1456 0 2512 5571 8922
 
题意:给定一些操作(0代表添加一个点,1代表删除一个点),求这些点的最远曼哈顿距离
 

以二维平面为例:

设距离最远的两点为 i, j,可知所求的最大距离必定有以下四种形式之一:

(xi-xj)+(yi-yj), (xj-xi)+(yi-yj), (xi-xj)+(yj-yi), (xj-xi)+(yj-yi) 变形一下,把相同点的坐标放到一起,

即 (xi+yi)-(xj+yj), (-xi+yi)-(-xj+yj), (xi-yi)-(xj-yj), (-xi-yi)-(-xj-yj),可以发现即去绝对值之后把同一点的坐标放在一起,对应坐标符号相同。

假如我们用0表示符号,用1表示正号,那么 (xi+yi) 可以表示为 11。

那么要表示一个维数为 dem 的所有状态,只需要用 0 ~ (2^dem-1) 的所有二进制就可以了。

于是只要对所有的点 (xi,yi),依次计算出 (xi+yi), (xi-yi), (-xi+yi), (-xi-yi)这四种形式,然后把每个点i算出来的这四种情况的最大值、最小值分别记录(更新)到数组 max[] 和 min[] 中,然后枚举每一种去绝对值的组合,组合后的最大值即为 answer。

 
要用到多集合容器(multiset);
一个集合(set)是一个容器,它其中所包含的元素的值是唯一的。
集和多集的区别是:set支持唯一键值,set中的值都是特定的,而且只出现一次;而multiset中可以出现副本键,同一值可以出现多次。
例如: mst[j].insert(sum); 插入一个元素; 则这个元素在容器可能有多个,不是唯一的。所以像set那样,一个值对应一个健值是不行星的,所以往往要多个健值来映射元素, 所以有 j和一个指向该元素的迭代器,这两个健值来映射这个元素sum.
另外几个函数功能。
begin() 返回指向第一个元素的 迭代器
end() 返回指向最后一个元素之后的 迭代器,不是最后一个元素 (要减1,才是指到最后一个元素)
clear() 清除所有元素
erase() 删除集合中的元素
find() 返回一个指向被查找到元素的 迭代器
insert() 在集合中插入元素
 

#include<iostream> #include<cstdio> #include<cstring> #include<set>

using namespace std;

const int N=60010; const double INF=1e20;

int n,m,num[N][10]; //map<int,int,greater<int> > mp[1<<5]; multiset<int> mst[1<<5]; multiset<int>::iterator it1,it2; //定义迭代器,它是一个地址

int main(){

    //freopen("input.txt","r",stdin);

    while(~scanf("%d%d",&n,&m)){         for(int i=0;i<(1<<5);i++)             mst[i].clear(); //清空容器         int od,x;         for(int i=1;i<=n;i++){             scanf("%d",&od);             if(od==0){                 for(int j=0;j<m;j++)                     scanf("%d",&num[i][j]);                 for(int j=0;j<(1<<m);j++){ //枚举每个点,组成的值。                     int sum=0;                     for(int k=0;k<m;k++){                         if(j&(1<<k))                             sum+=num[i][k];                         else                             sum-=num[i][k];                     }                     mst[j].insert(sum); //加入到容器中                 }             }else{                 scanf("%d",&x);                 for(int j=0;j<(1<<m);j++){ //算出要删除的点的sum值。                     int sum=0;                     for(int k=0;k<m;k++){                         if(j&(1<<k))                             sum+=num[x][k];                         else                             sum-=num[x][k];                     }                     it1=mst[j].find(sum); //在容器中找出这个值对应的迭代器                     mst[j].erase(it1); //删除这个迭代器指向的元素                 }             }             int ans=0;             //map<int,int>::iterator it1,it2;             for(int j=0;j<(1<<m);j++){                 it1=mst[j].end(); //最后一个元素的迭代器,要减一                 it1--;                 it2=mst[j].begin(); //第一个元素的迭代器                 ans=max(ans,(*it1)-(*it2)); //是地址,注意加*。             }             printf("%d\n",ans);         }     }     return 0; }

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值