zoukankan      html  css  js  c++  java
  • [JZOJ2679] 跨时代

    题目

    题目大意

    给你一堆边,你要将它们围成面积最大的矩形。
    边不一定要用完,而且围成的矩形不能凸出一块。
    (nleq 16)
    (l_i leq 15)


    思考历程

    看到这题的第一眼,就会立马往DP方面去想。
    我一开始的想法是求出每种选择的状态可以得到的长度,
    然后就可以得出哪些状态可以得到两条长度一样的边。
    接下来就开始匹配,找不相交的集合,如果两个都满足条件就统计入答案。
    但是我又想,上面的最后一个操作会不会使时间崩掉?
    于是我又想了几十分钟,迫不得已去打第三题和第二题了……
    打完后回来,仔细想想,发现可以折半搜索!
    (n)条边拆成两组,每一条边有(5)种选择,分别为不选和加入(a,b,c,d)四条边。
    (5^8)的时间枚举左边,然后钦定(aleq c)(bleq d)(如果不满足条件就踢掉,不用交换,因为交换后的情况是能够枚举到的)。我们以((c-a,d-b))作为关键字存在某个表中(用hash或map或数组,怎样都可以)。
    之所以存下它们的差,是因为在差相同的情况下,可以互补成完整的矩形。
    对于每个出现过的关键字,都给它分配一个数组,存下((a,b))
    然后就是枚举右边的情况,搞完之后以同样的方式在表中找,如果关键字出现过,就在分配给它的数组中枚举,将((a_i+c')(b_i+d'))统计入答案中。
    显然,最花时间的地方就是在数组中枚举,如果这个数组开得很大,有可能会拖慢速度。
    我试了一个(16)(8)的数据,就成功卡掉了。
    于是我便想到,数组中的重复元素其实是很多的。所以我将vector换成了set,那个数据就跑得贼快了。
    去掉了重复,我也不知道数据还有什么办法卡我,因为每个数组都不会特别大(根据数据范围感性地估计一下~)
    于是我就交了上去。果然AC,而且还跑得比较快。
    后来我又思考,万一数据真的卡我,该怎么办。于是我想到,同一个数组中若有(a_ileq a_j)(b_i leq b_j),显然((a_i,b_i))是可以不要的。那么在左边的枚举做完之后,我们将每个数组里的元素排序一遍,把其中这些没有意义的元素删去。于是我们就可以得到一个(a_i)递增,(b_i)递减的一个东西。显然我们要让(a_id'+b_ic')最大,就可以在数组上二分(或三分?)来做。反正时间复杂度是绝对可以保证的。


    正解

    其实这道题的方法有很多很多……折半搜索只是冰山一角。
    事实上我一开始想的、但却没有打的方法是对的。
    有的人直接搜索就过了,有的人背包、状压……
    所以就不一一说明了……
    于是“正解”这一栏有什么意义?


    代码

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <set>
    int n,m;
    int len[17];
    #define mo 1000007
    struct Node{
    	int key,num;	
    } h[mo];
    inline int my_hash(int ia1,int ia2){
    	int key=ia1*997+ia2+mo,i=key%mo;
    	for (;h[i].key!=key && h[i].key;i=(i+1==mo?0:i+1));
    	return i;
    }
    int cnt;
    set<pair<int,int> > arr[15000];
    void dfs1(int k,int a,int b,int c,int d){
    	if (k>m){
    		if (a>b || c>d)
    			return;
    		int w=my_hash(b-a,d-c);
    		if (!h[w].num)
    			h[w].num=++cnt;
    		arr[h[w].num].insert(make_pair(a,c));
    		return;
    	}
    	dfs1(k+1,a,b,c,d);
    	dfs1(k+1,a+len[k],b,c,d);
    	dfs1(k+1,a,b+len[k],c,d);
    	dfs1(k+1,a,b,c+len[k],d);
    	dfs1(k+1,a,b,c,d+len[k]);
    }
    int ans=0;
    void dfs2(int k,int a,int b,int c,int d){
    	if (k>n){
    		if (a>b || c>d)
    			return;
    		int w=my_hash(b-a,d-c);
    		if (h[w].num){
    			for (set<pair<int,int> >::iterator p=arr[h[w].num].begin();p!=arr[h[w].num].end();++p)
    				ans=max(ans,(b+p->first)*(d+p->second));
    		}
    		return;
    	}
    	dfs2(k+1,a,b,c,d);
    	dfs2(k+1,a+len[k],b,c,d);
    	dfs2(k+1,a,b+len[k],c,d);
    	dfs2(k+1,a,b,c+len[k],d);
    	dfs2(k+1,a,b,c,d+len[k]);
    }
    int main(){
    	scanf("%d",&n);
    	for (int i=1;i<=n;++i)
    		scanf("%d",&len[i]);
    	m=n>>1;
    	dfs1(1,0,0,0,0);
    	dfs2(m+1,0,0,0,0);
    	if (ans)
    		printf("%d
    ",ans);
    	else
    		printf("No Solution
    ");
    	return 0;
    }
    

    总结

    做题的时候不要怂,有时暴力的方法照样能过!

  • 相关阅读:
    css div position to parent
    linux 解压缩/压缩命令大全
    button with backgroundimage programmaticaly
    使用数组初始化vector 对象
    AudioServicesPlaySystemSound
    objective-c 中随机数的用法 (3种:arc4random() 、random()、CCRANDOM_0_1() )
    指针和多维数组(例子需要好好消化理解)
    很经典的赋值算法之一:动态为数组有序赋值
    string 类的c_str 的成员函数
    自由存储区的空间 C++和C
  • 原文地址:https://www.cnblogs.com/jz-597/p/11147571.html
Copyright © 2011-2022 走看看