基本什么都没复习(最多前一天晚上看了一下今年的Practice Round),然后就冲上来做了。
scoreboard中搜索hdu.toraoh,可以看我的当时实际提交情况。
题意就不翻译了,这种英文阅读应该是能搞掂的,不然真没法在IT外企工作了。
Problem A. Country Leader
设计一个结构体,能读入,读完顺手计算一下有多少不同的英文字母(注意英文字母不包括空格),然后顺手让这个结构体还能比较。最后不管你是O(nlogn)排序取最小,还是边读,边只保留最小,都行。
#include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <math.h> #include <algorithm> #include <set> using namespace std; typedef long long ll; int T,Case=1; struct Name{ char s[105]; int diff; void get(){ gets(s); set<char> ss; for(int i=0;s[i];++i){ if(s[i]!=' ')ss.insert(s[i]); } diff=ss.size(); } bool operator<(const Name&b)const{ if(diff!=b.diff)return diff>b.diff; return strcmp(s,b.s)<0; } }name[105]; int main(){ freopen("A-large.in","r",stdin); freopen("A-large.txt","w",stdout); int n; for(scanf("%d",&T);Case<=T;Case++){ scanf("%d%*c",&n); for(int i=0;i<n;i++)name[i].get(); sort(name,name+n); printf("Case #%d: ",Case); puts(name[0].s); } return 0; }
造成了比较大的精神伤害(和麻烦)的一题……
我的思路是,反其道而行之,你从高处流下来流到低洼处不走了,我反过来,找低洼处,从下面往上灌水,直到找不到能灌水的低洼处为止。
具体实现上,首先是预处理。
按高度块合并,然后把外圈靠海的那些块标记为和大海这个特殊块相连。
之后还做了一堆,比如和大海相连的块出发bfs,找更高的块,也标记为边缘块,等(是否有用?我不确定)。
然后在中间,不靠海的块按高度放入优先队列。
之后,每次从优先队列里抓一块出来,搜索其边缘块,看看这个面积区域积水能积多深。
如果不能积水(边缘块比这块还矮),这块就不考虑了。
否则,直接水灌进去,和边上同高度的合并起来。
如果还是不和边缘块在一起,放回优先队列,否则,也不应该灌水了,不放回优先队列。
这样一直处理,直到优先队列为空。
最后,统计灌进去多少水,输出。
——你看我洋洋洒洒扯了这么麻烦的一个办法,然后我代码也有200行这么长……
#include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <math.h> #include <algorithm> #include <queue> #include <map> using namespace std; typedef long long ll; const int di[4][2]={{-1,0},{1,0},{0,1},{0,-1}}; int T,Case=1; int r,c; int orimap[55][55]; int tarmap[55][55]; int fat[3000]; int ptoid(int x,int y){ return x*c+y; } int find(int x){ return fat[x]==x?x:fat[x]=find(fat[x]); } bool join(int a,int b){ int fa=find(a),fb=find(b); if(fa==fb)return false; fat[fa]=fb; return true; } struct Point{ int x,y; Point(){} Point(int a,int b):x(a),y(b){} int ptoid(){ return x*c+y; } Point go(int d){ return Point(x+di[d][0],y+di[d][1]); } bool inrange(){ return x>=0 &&x<r&&y>=0&&y<c; } bool operator<(const Point&b)const{ if(x!=b.x)return x<b.x; return y<b.y; } }; bool vis[55][55]; int getMinHeight(Point p){ memset(vis,false,sizeof(vis)); int res=INT_MAX; queue<Point> q; q.push(p); vis[p.x][p.y]=true; while(!q.empty()){ Point x=q.front(); q.pop(); for(int i=0;i<4;i++){ Point p2=x.go(i); if(vis[p2.x][p2.y])continue; if(find(p2.ptoid())!=find(p.ptoid())){ res=min(res,tarmap[p2.x][p2.y]); }else{ vis[p2.x][p2.y]=1; q.push(p2); } } } return res; } void setMinHeight(Point p,int h){ memset(vis,false,sizeof(vis)); queue<Point> q; q.push(p); vis[p.x][p.y]=true; tarmap[p.x][p.y]=h; vector<Point> joinList; while(!q.empty()){ Point x=q.front(); q.pop(); for(int i=0;i<4;i++){ Point p2=x.go(i); if(vis[p2.x][p2.y])continue; if(find(p2.ptoid())!=find(p.ptoid())){ if(tarmap[p2.x][p2.y]==h){ joinList.push_back(p2); } }else{ tarmap[p2.x][p2.y]=h; vis[p2.x][p2.y]=1; q.push(p2); } } } for(int i=0;i<joinList.size();i++){ join(joinList[i].ptoid(),p.ptoid()); } } void joinHeigher(Point p){ memset(vis,false,sizeof(vis)); queue<Point> q; q.push(p); vis[p.x][p.y]=true; while(!q.empty()){ Point x=q.front(); q.pop(); for(int i=0;i<4;i++){ Point p2=x.go(i); if(!p2.inrange())continue; if(vis[p2.x][p2.y])continue; if(orimap[p2.x][p2.y]>orimap[x.x][x.y]){ join(p2.ptoid(),x.ptoid()); q.push(p2); } } } } int main(){ freopen("B-large.in","r",stdin); freopen("B-large.txt","w",stdout); for(scanf("%d",&T);Case<=T;Case++){ scanf("%d%d",&r,&c); for(int i=0;i<=r*c+1;++i)fat[i]=i; for(int i=0;i<r;i++){ for(int j=0;j<c;j++){ scanf("%d",&orimap[i][j]); tarmap[i][j]=orimap[i][j]; } } int boundId=ptoid(r,0); for(int i=0;i<r;i++){ join(boundId,ptoid(i,0)); joinHeigher(Point(i,0)); join(boundId,ptoid(i,c-1)); joinHeigher(Point(i,c-1)); } for(int i=0;i<c;i++){ join(boundId,ptoid(0,i)); joinHeigher(Point(0,i)); join(boundId,ptoid(r-1,i)); joinHeigher(Point(r-1,i)); } for(int i=1;i<r-1;i++){ for(int j=1;j<c-1;j++){ for(int k=0;k<4;k++){ int px=i+di[k][0]; int py=j+di[k][1]; if(orimap[px][py]==orimap[i][j]){ join(ptoid(i,j),ptoid(px,py)); } } } } priority_queue< pair<int,Point>,vector<pair<int,Point>>,greater<pair<int,Point>>> q; for(int i=1;i<r-1;i++){ for(int j=1;j<c-1;j++){ if(find(ptoid(i,j))==ptoid(i,j) && ptoid(i,j)!=find(boundId)){ q.push(make_pair(orimap[i][j],Point(i,j))); } } } while(!q.empty()){ pair<int,Point> x=q.top();q.pop(); if(find(x.second.ptoid())!=x.second.ptoid())continue; int h=getMinHeight(x.second); if(h<tarmap[x.second.x][x.second.y])continue; setMinHeight(x.second,h); if(find(x.second.ptoid())!=find(boundId)){ q.push(make_pair(h,x.second)); } } int ans=0; for(int i=1;i<r-1;i++){ for(int j=1;j<c-1;j++){ ans+=tarmap[i][j]-orimap[i][j]; } } printf("Case #%d: %d\n",Case,ans); } return 0; }
这题的重点是,不要被数学公式吓跑,不要考虑求导,把题读完。
题目描述部分的最后一句话是:
It is guaranteed that -1 < r < 1, and there is exactly one solution in each test case.
r=-1时,显然有f(-1)>=0,要有且仅有一解,显然f(1)<=0不说,在解和-1之间必然同号,解和1之间也是同号——那就是保证有[-1,x)是正的,(x,1]是负的。
那很直白的思路了:二分查找,是正的,缩小解区间左边,否则缩小解区间右边。
注意到这题要求精度达到1e-9,这不是什么低精度要求……
所以,二分的绝对误差要求高一点,中间的计算尽量用高精度的数据类型(听说C/C++要long double才能过,我在似乎无所谓的地方用了一下,也过了╮(╯_╰)╭)
——Google你真的没把B和C放反吗?
#include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <math.h> #include <algorithm> using namespace std; typedef long long ll; int T,Case=1; int n; int val[105]; long double cal(double x){ long double v=-val[0]*pow(1+x,n); for(int i=1;i<=n;i++){ v+=val[i]*pow(1+x,n-i); } return v; } int main(){ freopen("C-large.in","r",stdin); freopen("C-large.txt","w",stdout); for(scanf("%d",&T);Case<=T;Case++){ scanf("%d",&n); for(int i=0;i<=n;i++)scanf("%d",&val[i]); double l=-1.0,r=1.0,mid; while(r-l>1e-12){ mid=(l+r)/2; if(cal(mid)>0.0)l=mid; else r=mid; } printf("Case #%d: %.12f\n",Case,l); } return 0; }
事实上我被以前大家的过题情况骗了,然后想着小数据分拿到就好……
完全相反的是,D的大数据的分数是很好拿到的,你不需要成为动态规划玩得很溜的大神。D的大数据其实是,套路。
D的小数据的直白过法就是,分组背包。
然后我莫名其妙地开始脑抽,打死都要swap、clear,坑了好半天,最后剩20分钟的时候还是调通了……
代码很短,这个写法也不是这题的重点,贴一下,感兴趣自己上网查分组背包这东西。
#include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <math.h> #include <algorithm> #include <vector> using namespace std; typedef long long ll; int T,Case=1; int m,n; int Ki[10],Li[10]; int A[10][15],C[10][15]; vector<int> last(1005),cur(1005); int main(){ freopen("D-small-attempt1.in","r",stdin); freopen("D-small1.txt","w",stdout); for(scanf("%d",&T);Case<=T;Case++){ scanf("%d%d",&m,&n); for(int i=0;i<n;i++){ scanf("%d%d",&Ki[i],&Li[i]); for(int j=1;j<=Ki[i];j++)scanf("%d",&A[i][j]); for(int j=2;j<=Ki[i];j++)scanf("%d",&C[i][j]); } fill(last.begin(),last.end(),0); for(int i=0;i<n;i++){ last[0]+=A[i][Li[i]]; } for(int i=1;i<=m;i++)last[i]=last[0]; for(int i=0;i<n;i++){ int premon=0; cur=last; for(int j=Li[i]+1;j<=Ki[i];++j){ premon+=C[i][j]; for(int l=m;l>=premon;--l){ cur[l]=max(max(cur[l],last[l]),last[l-premon]+A[i][j]-A[i][Li[i]]); } } last=cur; } printf("Case #%d: %d\n",Case,last[m]); } return 0; }
注意到,N<=12,必须选8个
C(12,8)=495,很小啊。
然后考虑枚举选择的组合之后,怎么办?
还记得Practice Round的Problem B. Robot Rock Band吗?怎么做大数据呢?
4组,分成2部分,AB和CD,AB暴力计算完,放进HashMap,待用,之后枚举CD,在AB的HashMap中找,累加结果,复杂度变成了O(n^2+n^2*(1~logn))=O(n^2)
这种思想呢,你可以称作,meet in the middle。
概括起来就是,左边一半管自己的,怎么暴力怎么来,右边一半也是如此。把左右两边的结果结合起来的时候,使用一些高效的办法(单次操作一般O(1)、O(logn)的办法)。
这种思路能借鉴过来吗?Yes!
------以下是参考思路------
选出8个后,分成2部分处理,前4种卡牌和后4种卡牌。
对前4种卡牌,先暴力枚举,因为只有10档升级,所以最多有10^4种情况。
为了效率,以及之后与后半部分的结合的便捷性,我们考虑只留下有效数据。
所谓有效数据,就是对这种组合方案,不存在比他便宜或一样便宜的东西,战斗力更强。
很简单。
1、对之前算出来不超预算的情况,按价格从小到大,同价格按战力从大到小排序。
2、排序后,从前到后一个个看,记录前面最高战力,只保留能更新最高战力的数据(想一想为什么)
那对后半部分的处理就比较显然了。
同样的暴力枚举,是否只留下有效数据看你个人喜好(影响不大,这边再这么操作,那也只是常数优化)
对每种组合方案,在前4种卡牌中,找价格不超过剩余钱数的,战力最大的方案。根据这个结果,更新最优方案。
之前排序处理好有效数据的话,用二分查找,一次查找时间复杂度只要O(logn)。
最后考虑一下,我们的程序多久能跑完:
每组数据,要枚举C(12,8)=495种情况
每种情况下,计算量为10^4(前半部分的生成)+10^4*log(10^4)(排序)+10^4(筛选有效数据)+10^4*log(10^4)(后半部分的生成与二分查找)~= 280000
所以,一组数据大致计算量为495*280000=1.386e8
考虑到现在的电脑一般能做到1秒计算1.8e8(1秒计算1e8的,那是奔腾4),也就意味着,一组数据能在1秒之内计算出来,100组数据也就100秒,
你可以先小数据上确认正确性,然后下载大数据,运行,上传——别忘了大数据提交时间窗口是8分钟,跑100秒也不紧张。
实际上对Practice模式提供的大数据,我只跑了20秒,当然,我的代码里还有很多常数优化空间。
#include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <math.h> #include <algorithm> #include <vector> using namespace std; typedef long long ll; int T,Case=1; ll m,n; int Ki[15],Li[15]; ll A[15][15],C[15][15]; ll bestans; bool sel[15]; int itemList[8]; struct Item{ ll money,power; Item(){} Item(ll a,ll b):money(a),power(b){} bool operator<(const Item&b)const{ if(money!=b.money)return money<b.money; return power>b.power; } }; vector<Item> tmp,t1; void check(){ tmp.clear();t1.clear(); for(int i=Li[itemList[0]];i<=Ki[itemList[0]];++i) for(int j=Li[itemList[1]];j<=Ki[itemList[1]];++j) for(int k=Li[itemList[2]];k<=Ki[itemList[2]];++k) for(int l=Li[itemList[3]];l<=Ki[itemList[3]];++l){ Item nnew(C[itemList[0]][i]+C[itemList[1]][j]+C[itemList[2]][k]+C[itemList[3]][l], A[itemList[0]][i]+A[itemList[1]][j]+A[itemList[2]][k]+A[itemList[3]][l]); if(nnew.money<=m)tmp.push_back(nnew); } sort(tmp.begin(),tmp.end()); ll maxpow=0; for(int i=0;i<tmp.size();i++){ if(tmp[i].power<=maxpow)continue; maxpow=tmp[i].power; t1.push_back(tmp[i]); } for(int i=Li[itemList[4]];i<=Ki[itemList[4]];++i) for(int j=Li[itemList[5]];j<=Ki[itemList[5]];++j) for(int k=Li[itemList[6]];k<=Ki[itemList[6]];++k) for(int l=Li[itemList[7]];l<=Ki[itemList[7]];++l){ Item nnew(C[itemList[4]][i]+C[itemList[5]][j]+C[itemList[6]][k]+C[itemList[7]][l], A[itemList[4]][i]+A[itemList[5]][j]+A[itemList[6]][k]+A[itemList[7]][l]); if(nnew.money<=m){ Item nn2(m-nnew.money,0); auto it=upper_bound(t1.begin(),t1.end(),nn2); //while(it!=t1.end()&&it->money+nnew.money<m)++it; while(it==t1.end()||it->money+nnew.money>m)--it; auto x=*it; bestans=max(bestans,x.power+nnew.power); } } } void dfs(int depth,int last){ if(depth==8){ check(); return ; } if(8-depth>n-last)return; for(int i=last+1;i<n;i++){ itemList[depth]=i; dfs(depth+1,i); } } int main(){ freopen("D-large-practice.in","r",stdin); freopen("D-large.txt","w",stdout); for(scanf("%d",&T);Case<=T;Case++){ bestans=0; scanf("%I64d%I64d",&m,&n); for(int i=0;i<n;i++){ scanf("%d%d",&Ki[i],&Li[i]); for(int j=1;j<=Ki[i];j++)scanf("%I64d",&A[i][j]); for(int j=2;j<=Ki[i];j++)scanf("%I64d",&C[i][j]); C[i][Li[i]]=0; for(int j=Li[i]+1;j<=Ki[i];j++)C[i][j]+=C[i][j-1]; } dfs(0,-1); printf("Case #%d: %I64d\n",Case,bestans); } return 0; }
啰嗦了这么多呢……辛苦阅读到这里的各位了。