基本什么都没复习(最多前一天晚上看了一下今年的Practice Round),然后就冲上来做了。
scoreboard中搜索hdu.toraoh,可以看我的当时实际提交情况。
题意就不翻译了,这种英文阅读应该是能搞掂的,不然真没法在IT外企工作了。
本文地址:http://blog.csdn.net/fcxxzux/article/details/51873820
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;
}
Problem B. Rain
造成了比较大的精神伤害(和麻烦)的一题……
我的思路是,反其道而行之,你从高处流下来流到低洼处不走了,我反过来,找低洼处,从下面往上灌水,直到找不到能灌水的低洼处为止。
具体实现上,首先是预处理。
按高度块合并,然后把外圈靠海的那些块标记为和大海这个特殊块相连。
之后还做了一堆,比如和大海相连的块出发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;
}
Problem D. Clash Royale
事实上我被以前大家的过题情况骗了,然后想着小数据分拿到就好……
完全相反的是,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;
}
重点是,D的大数据。
注意到,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;
}
啰嗦了这么多呢……辛苦阅读到这里的各位了。