题目链接:2017 ACM-ICPC 亚洲区(南宁赛区)网络赛

A. Weather Patterns

题意:有四种天气,给你一个4*4的矩阵,a[i][j]表示从天气i转变为天气j的概率,然后又四个询问(两种),第一种是,给你两个观察序列,问你按照这个观察序列转变的概率是多少,第二种是给你i和j,表示连续若干天都是i天气或j天气的期望为多少
解析:求概率,那不就是一直乘么,求期望,那不就是​​​几何分布​​的期望么,您就欺负我英语不好吧!!!

#include <bits/stdc++.h>
using namespace std;
double pc[5][5];
int md[2][1000000];
int len[2];
int x[2];
double ans;

int main(void)
{
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
scanf("%lf",&pc[i][j]);
len[0]=0;
for(int i=0;true;i++)
{
scanf("%d",&md[0][i]);
len[0]++;
if(getchar()=='\n')
break;
}
len[1]=0;
for(int i=0;true;i++)
{
scanf("%d",&md[1][i]);
len[1]++;
if(getchar()=='\n')
break;
}
scanf("%d%d",&x[0],&x[1]);
ans=1.0;
for(int i=1;i<len[0];i++)
ans*=pc[md[0][i-1]][md[0][i]];
printf("%.8f\n",ans);
ans=1.0;
for(int i=1;i<len[1];i++)
ans*=pc[md[1][i-1]][md[1][i]];
printf("%.8f\n",ans);
printf("%.8f\n",1.0/(1.0-pc[x[0]][x[0]]));
printf("%.8f\n",1.0/(1.0-pc[x[1]][x[1]]));
return 0;
}

B. Train Seats Reservation

题意:有一辆火车,有100个火车站台,站台编号为1~100,每个乘客能预定k张火车票,从t1站台上车,从t2站台下车,问你这两火车最少要多少个座位才能满足说有乘客的要求
解析:直接开个数组记录下每个站台的时候有多少个座位被座了,然后算一下最大值输出就可以了,记住先下后上(重点)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll vis[105];
int main(void)
{
int n;
while(scanf("%d",&n))
{
if(n==0)
{
puts("*");
break;
}
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++)
{
int s,t;
ll k;
scanf("%d %d %lld",&s,&t,&k);
for(int j=min(s,100);j<min(100,t);j++)
vis[j] += k;
}
ll ans = 0;
for(int i=1;i<=100;i++)
ans = max(ans,vis[i]);
printf("%lld\n",ans);
}
return 0;
}

D. Path Search with Constraints

题意:好像是求什么(1,1)到(I,J)的最短路径的,要求权值最小,根据样例得出的题意大概就是,给你一个矩阵,题面又告诉你了一个转移方程,然后让你输出最小花费,和路径
解析:大概猜出题意了,想着,动态转移方程都告诉我了,直接套不就好了么,输出路径的时候发现,和答案一点都不想,看了半天题目,猜测是有两个状态要多存一个结点吧。试着交了一下,A了,真的是猜了半年的题意:)

#include<bits/stdc++.h>
using namespace std;
const int INF=0x7ffffff;
double dp[40][40];
int marix[40][40];
char as[1000];
pair<int,int> pre[40][40];
int main()
{
double a,b;
scanf("%lf%lf",&a,&b);
for(int i=0; i<40; i++)
{
for(int j=0; j<40; j++)
dp[i][j]=INF;
}
int c1 = 1,c2 = 1;
getchar();
while(c1<=6&&gets(as))
{
c2 = 1;
int len = strlen(as);
for(int i=0; i<len;)
{
if(as[i]>='0' && as[i]<='9')
{
int tmp = 0;
while(as[i]>='0' && as[i]<='9' && i<len)
{
tmp = tmp*10+as[i]-'0';
i++;
}
marix[c1][c2++] = tmp;
}
else
i++;
}
c1++;
}
dp[1][1]=marix[1][1];
pre[1][1]=make_pair(0,0);
for(int i=1; i<c1; i++)
{
for(int j=1; j<c2; j++)
{
if(j>2&&i>1)
{
if(dp[i-1][j-2]+a*(marix[i][j-1]+marix[i][j])<dp[i][j])
{
dp[i][j]=dp[i-1][j-2]+a*(marix[i][j-1]+marix[i][j]);
pre[i][j]=make_pair(i-1,j-2);
}
}
if(i>1&&j>1)
{
if(dp[i-1][j-1]+b*marix[i][j]<dp[i][j])
{
dp[i][j]=dp[i-1][j-1]+b*marix[i][j];
pre[i][j]=make_pair(i-1,j-1);
}
}
if(j>1&&i>2)
{
if(dp[i-2][j-1]+a*(marix[i-1][j]+marix[i][j])<dp[i][j])
{
dp[i][j]=dp[i-2][j-1]+a*(marix[i-1][j]+marix[i][j]);
pre[i][j]=make_pair(i-2,j-1);
}
}
}
}
vector<pair<int,int> >ans;
printf("%.6f\n",dp[c1-1][c2-1]);
int x = c1-1,y = c2-1;
ans.push_back(make_pair(x,y));
while(x!=1 || y!=1)
{
int tx = pre[x][y].first;
int ty = pre[x][y].second;
if(x-tx==2)
ans.push_back(make_pair(x-1,y));
else if(y-ty==2)
ans.push_back(make_pair(x,y-1));
x = tx,y = ty;
ans.push_back(make_pair(tx,ty));
}
// for(int i=1;i<c1;i++)
// {
// for(int j=1;j<c2;j++)
// printf("%.1f ",dp[i][j]==INF?0:dp[i][j]);
// puts("");
// }
// for(int i=1;i<c1;i++)
// {
// for(int j=1;j<c2;j++)
// printf("(%d,%d) ",pre[i][j].first,pre[i][j].second);
// puts("");
// }
for(int i=ans.size()-1;i>=0;i--)
printf("(%d,%d)\n",ans[i].second,ans[i].first);
return 0;
}

F. Overlapping Rectangles

题意:矩形面积并
解析:线段树,直接套模板

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4;
typedef long long ll;
int n;
struct node
{
ll l, r, h;
int d;
node() {}
node(int l, int r, int h, int d): l(l), r(r), h(h), d(d) {}
bool operator< (const node& rhs) const
{
return h < rhs.h;
}
} a[maxn];
ll cnt[maxn << 2];
ll sum[maxn << 2],all[maxn];
void push_up(int l, int r, int rt)
{
if(cnt[rt]) sum[rt] = all[r + 1] - all[l];
else if(l == r) sum[rt] = 0;
else sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void update(int L, int R, int v, int l, int r, int rt)
{
if(L <= l && r <= R)
{
cnt[rt] += v;
push_up(l, r, rt);
return;
}
int m = (l + r)>>1;
if(L <= m) update(L, R, v, l, m, rt << 1);
if(R > m) update(L, R, v, m + 1, r, rt << 1 | 1);
push_up(l, r, rt);
}
int main()
{
while(scanf("%d", &n))
{
if(n==0)
{
puts("*");
break;
}
for(int i = 1; i <= n; ++i)
{
ll x1, y1, x2, y2;
scanf("%lld%lld%lld%lld", &x1, &y1, &x2, &y2);
a[i] = node(x1, x2, y1, 1);
a[i + n] = node(x1, x2, y2, -1);
all[i] = x1;
all[i + n] = x2;
}
n <<= 1;
sort(a + 1, a + 1 + n);
sort(all + 1, all + 1 + n);
int m = unique(all + 1, all + 1 + n) - all - 1;

memset(cnt, 0, sizeof cnt);
memset(sum, 0, sizeof sum);

ll ans = 0;
for(int i = 1; i < n; ++i)
{
int l = lower_bound(all + 1, all + 1 + m, a[i].l) - all;
int r = lower_bound(all + 1, all + 1 + m, a[i].r) - all;
if(l < r) update(l, r - 1, a[i].d, 1, m, 1);
ans += sum[1] * (a[i + 1].h - a[i].h);
}
printf("%lld\n",ans);
}
return 0;
}

G. Finding the Radius for an Inserted Circle

题意:给你三个半径为R的圆(Ca,Cb,Cc),然后根据这三个圆生成C1,即和之前三个圆相切,然后C2也是类似这样生成的,总之看图就好啦,要求的就是Ck的半径,要直接截断小数点后面的数

解析:其实第一个圆很好求,如下图所示


对于第2个圆,一直到第k个圆来说,求法是一样的,如下图所示

#include <bits/stdc++.h>
using namespace std;
double ans[15];
int main(void)
{
int l,k;
double r;
scanf("%d",&l);
scanf("%lf",&r);
ans[1] = r*2.0/sqrt(3.0)-r;
double d = (ans[1]+r)/2.0-ans[1];
for(int i=2;i<=10;i++)
{
ans[i] = d*d/2.0/(r+d);
d -= 2*ans[i];
}
l++;
while(l--)
{
scanf("%d",&k);
if(k==-1)
break;
printf("%d %d\n",k,(int)floor(ans[k]));
}
return 0;
}

H. A Cache Simulator

题意:模拟一个缓存器
解析:表示好险队友是计科专业的

#include <bits/stdc++.h>
using namespace std;
int main(void)
{
int mcache[64];
memset(mcache,0,sizeof(mcache));
int acnt=0,hcnt=0;
while(1)
{
char c[10];
scanf("%s",c);
if(c[0]=='E'&&c[1]=='N')
{
double ans=100.0*hcnt/acnt;
printf("Hit ratio = %.2f%c",ans,'%');
break;
}
int ad=0;
for(int i=0;i<7;i++)
{
int k;
if(c[i]>='A'&&c[i]<='F')
k=c[i]-'A'+10;
if(c[i]>='0'&&c[i]<='9')
k=c[i]-'0';
ad=ad*16+k;
}
int gid=(ad/16)%64;
if(abs(mcache[gid]-ad)<16)
{
printf("Hit\n");
hcnt++;
}
else
{
printf("Miss\n");
mcache[gid]=ad;
}
acnt++;
}
return 0;
}

I. GSM Base Station Identification

题意:给你10个坐标,问你这10个点在哪个正六边形里面(题目的图片上)
解析:当初感觉这题很麻烦不是特别想写,比赛结束后,发现可以通过把每个正六边形的中心表示出来,然后枚举所有正六边形的中心,判断两点间的距离是是否小于5

#include <bits/stdc++.h>
using namespace std;
struct point
{
double x,y;
point() {}
point(double _x,double _y):x(_x),y(_y) {}
}ans[15],p;
double dis(point p1,point p2)
{
return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
int main(void)
{
for(int i=0;i<10;i++)
{
scanf("%lf %lf",&p.x,&p.y);
for(int j=-9;j<=10;j++)
{
for(int k=-9;k<=10;k++)
{
//自己在草稿纸上画一下,应该就能发现,六边形的中心坐标满足
point tmp;
tmp.x = 5.0*sqrt(3.0)*j+5*sqrt(3.0)/2.0*k;
tmp.y = 15.0/2.0*k;
if(dis(tmp,p)<=5.0)
ans[i] = point(j,k);
}
}
}
for(int i=0;i<10;i++)
{
if(i)printf(", ");
printf("[%.f,%.f]",ans[i].x,ans[i].y);
}
puts("");
return 0;
}

J. Minimum Distance in a Star Graph

题意:给你5组长度为n的字符串s和e,问你e要执行多少次交换操作才能使得s和e相等,交换操作的定义为:第一个位置的数字和其他交换,仅此而已
解析:直接bfs即可,(一开始bfs不敢交,后面试了一下,发现竟然是WA了,在队友的提醒下,发现自己看错了题目,以为可以任意换。。。。

#include <bits/stdc++.h>
using namespace std;
map<long long ,int>maple;
struct node
{
char s[15];
long long v;
int step;
}a,b;
int n;
long long geth(char a[])
{
long long res = 0;
for(int i=0;i<n;i++)
res = res*10+a[i]-'0';
return res;
}
int bfs()
{
a.v = geth(a.s);
a.step = 0;
queue<node>q;
q.push(a);
maple[a.v] = 1;
while(!q.empty())
{
node now = q.front();
q.pop();
if(strcmp(now.s,b.s)==0)
return now.step;
for(int j=1;j<n;j++)
{
node tmp = now;
swap(tmp.s[0],tmp.s[j]);
tmp.v = geth(tmp.s);
if(maple.find(tmp.v)!=maple.end())
continue;
maple[tmp.v] = 1;
tmp.step = now.step+1;
q.push(tmp);
}
}
return -1;
}
int main(void)
{
int m = 5;
scanf("%d",&n);
for(int kk=0;kk<m;kk++)
{
maple.clear();
scanf("%s %s",a.s,b.s);
printf("%d\n",bfs());
}
return 0;
}

L. The Heaviest Non-decreasing Subsequence Problem

题意:让你求一个最重的不严格递增子序列,也就是给你一个长度要你自己算的序列,每一个序列有一个权值,即该元素为负数,权值为0,该元素>=10000,权值为5,否则权值为1,然后让你求一个最大权值和的子序列(不减)
解析:其实把>=10000的元素,重复5次加到原序列中,然后求一次最长不减子序列即可

#include <bits/stdc++.h>
using namespace std;
const int INF=1<<30;
const int maxn=1000007;

int a[300000];
int b[maxn];
int len,n;
int v[300000];
int dp[maxn];

int main(void)
{
int x;
len=0;
while(scanf("%d",&x)!=-1)
{
if(x>=10000)
{
v[len]=5;
a[len]=x%10000;
}
else if(x<0)
{
v[len]=0;
a[len]=x;
}
else
{
v[len]=1;
a[len]=x;
}
len++;
}
n=0;
for(int i=0;i<len;i++)
for(int j=0;j<v[i];j++)
b[n++]=a[i];
fill(dp,dp+n,INF);
for(int i=0;i<n;i++)
{
*upper_bound(dp,dp+n,b[i])=b[i];
}
printf("%d\n",lower_bound(dp,dp+n,INF)-dp);
return 0;
}

M. Frequent Subsets Problem

题意:给以一个全集U={1,2,…,n},然后又m个全集的子集,现在给你一个α,问你存在几个集合,在这m个集合出现的次数大于等于α*m,输入第一行是n和α,接下来m行分别是m个集合
解析:题目看懂后,应该算是比较好做吧,除去输入不说以外,我的想法是,记录1~n个元素,每个元素在这m个集合里出现的次数,然后答案的集合肯定是这些元素(出现次数大于ceil(α*m))组合起来,那么直接把这些数全都放到一个集合里,然后枚举这个集合的子集是否满足条件,如果满足ans++

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int maxn = 1e5+100;
int s[maxn];
int vis[maxn];
char a[maxn];
int main(void)
{
int n;
double b;
scanf("%d %lf",&n,&b);
int m = 0;
getchar();
while(gets(a))
{

int len = strlen(a);
for(int i=0;i<len;)
{
if(a[i]>='0'&&a[i]<='9')
{
int tmp = 0;
while(a[i]>='0'&&a[i]<='9'&&i<len)
{
tmp = tmp*10+a[i]-'0';
i++;
}
vis[tmp]++;
s[m] |= 1<<(tmp-1);
}
else
i++;
}
m++;
}
int t = (int)ceil(b*m);
int res = 0;
for(int i=1;i<=n;i++)
{
if(vis[i]>=t)
res |= 1<<(i-1);
}
int ans = 0;
int sub = res;
do
{
int flag=0;
for(int i=0;sub!=0&&i<m;i++)
if((sub|s[i])==s[i])
flag++;
if(flag>=t)
{
ans++;
// printf("%o\n",sub);
}
sub = (sub-1)&res;
}while(sub!=res);
printf("%d\n",ans);
return 0;
}