World Finals 2017 (水题题解)
看大佬做2017-WF,我这种菜鸡,只能刷刷水题,勉强维持生活。 赛后补补水题。
题目pdf链接,中文的,tls翻译的,链接在这里
个人喜欢在vjudge上面刷题。
E Need for Speed
题意:
有中文题意,我就不多说了,仪表盘会有一个固定偏差,求这个。
思路:
二分答案,进行判断,二分的上下限,我是 -1 到 1e8;一开始范围错了WA掉了。
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
typedef long long int LL;
const int INF=2e9+1e8;
const int maxn=1e6+100;
const double eps=1e-8;
int di[maxn],si[maxn],n,T;
bool judge(double mid)
{
double ans=0;
for(int i=0; i<n; i++)
{
ans+=di[i]/(si[i]+mid);
}
// printf("mid=%lf ans=%lf\n",mid,ans);
return ans>=T;
}
double solve(double l,double r)
{
double ans=-100000;
while((l<=r)&&(r-l>eps))
{
double mid=(l+r)/2;
//printf("l=%")
// printf("mid=%lf flag=%d\n",mid,judge(mid));
if(judge(mid)) l=mid;
else r=mid;
ans=mid;
}
return ans;
}
//#define JJ -0.508653377
int main()
{
// printf("%lf\n",5/(3+JJ)+2/(2+JJ)+3/(6+JJ)+3/(1+JJ));
scanf("%d%d",&n,&T);
int l=INF;
for(int i=0; i<n; i++)
{
scanf("%d%d",&di[i],&si[i]);
l=min(si[i],l);
}
printf("%lf\n",solve(double(-l),100000000.));
return 0;
}
Problem I :Secret Chamber at Mount Rushmore
题目描述:
有一组转化关系,如果字符 a->b, b->c, 那么 a->c 也成立。问,给定一些关系,前面的字符串能否转为后面的字符串? yes:no
题目思路:
一开始预处理出,任意两点是否能到达。枚举每一个起点进行dfs。后面\(O(1)\)查询。
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <queue>
using namespace std;
typedef long long int LL;
const int INF=2e9+1e8;
const int maxn=1e6+100;
const double eps=1e-8;
struct Node
{
char t;
int next;
}edge[maxn];
int first[265],sz;
void init()
{
memset(first,-1,sizeof(first));
sz=0;
}
void addedge(char s,char t)
{
edge[sz].t=t,edge[sz].next=first[(int)s];
first[(int)s]=sz++;
}
bool maze[265][265];
bool vis[264];
void dfs(int now,int to)
{
maze[now][to]=1;
vis[to]=1;
for(int i=first[to];i!=-1;i=edge[i].next)
{
int t=edge[i].t;
if(vis[t]==0) dfs(now,t);
}
}
int main()
{
init();
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
char s[5],t[5];
scanf("%s%s",s,t);
addedge(s[0],t[0]);
}
memset(maze,0,sizeof(maze));
for(char i='a';i<='z';i++)
{
memset(vis,0,sizeof(vis));
dfs(i,i);
}
while(m--)
{
char a[100],b[100];
int lena,lenb;
scanf("%s%s",a,b);
lena=strlen(a),lenb=strlen(b);
if(lena!=lenb) printf("no\n");
else
{
bool flag=true;
for(int i=0;i<lena;i++)
{
if(maze[(int)a[i]][(int)b[i]]==0)
{
flag=false;
break;
}
}
if(flag) printf("yes\n");
else printf("no\n");
}
}
return 0;
}
题目链接:C - Mission Improbable
题目意思:
给你一个平面图代表每一个位置的箱子个数,问做多拿走多少个箱子,三视图不变?
解题思路:
看了题解慢慢懂的,问题在于求哪些箱子需要保留下来。才能保证三视图不变。首先我们想俯视图,只需要让有的地方留一个箱子即可。其次,正视图,侧视图。我们可以这样子思考,先不管正视图。我侧视图,选完了再说。那么侧视图需要保留的个数就是每行的最大值减一。
那么现在来考虑正视图。正视图应该如何选择才能使得选择最少并且满足条件? 如果侧视图选中了正视图的最大值那么正视图就不需要额外再选择。刚好,二分图匹配能解决,我们让二分图左边是行号,右边是列号。当某个位置既是行的最大值又是列的最大值,那么我们就在i行与j列之间建一条边。跑一遍匈牙利算法之后,匹配是最大的,意思就是让尽可能多的行列公用一个元素,之后对于无法公用的列那就让该列选择该列的最大值即可。
弱弱代码,仅供参考。
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <set>
#include <map>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long LL;
const int INF = 2e9 + 1e8;
const int MOD = 1e9 + 7;
const double eps = 0.0000000001;
void fre()
{
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
}
#define MSET(a, b) memset(a, b, sizeof(a))
#define fr(i, a, n) for (int i = a; i < n; i++)
const int maxn = 150;
int mat[maxn][maxn];
int side[maxn], front[maxn];
int G[maxn][maxn];
int vis[maxn], link[maxn], n, m;
int match(int x)
{
for (int i = 1; i <= m; i++)
{
if (!vis[i] && G[x][i])
{
vis[i] = 1;
if (!link[i] || match(link[i]))
{
link[i] = x;
return 1;
}
}
}
return 0;
}
int main()
{
scanf("%d%d", &n, &m);
LL sum = 0, sub = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
scanf("%d", &mat[i][j]);
side[i] = max(side[i], mat[i][j]);
front[j] = max(front[j], mat[i][j]);
if (mat[i][j])
sub++;
sum += mat[i][j];
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (mat[i][j] && side[i] == front[j])
G[i][j] = 1;
}
}
for (int i = 1; i <= n; i++)
{
memset(vis, 0, sizeof(vis));
match(i);
}//匹配
for (int i = 1; i <= n; i++)
if (side[i])
sub += side[i] - 1; //统计侧视图
for (int i = 1; i <= m; i++)
if (!link[i] && front[i])
sub += front[i] - 1; //把没匹配的额外加;
printf("%lld\n", sum - sub);
return 0;
}
--------- D - Money for Nothing -------------
题目描述:
2017 World Final D,原本的题意就不说,我转化一下就是。给定两个点的集合set:\(A\);set:\(B\),任意在两个集合中各选一个,求最大的 \(max\left ( \left ( x_B - x_A \right )\times \left ( y_B-y_A \right ) \right )\);再和0取一下最大值(即为:最大值为负数输出0)。
解题思路:
参见了大佬的解法,可以把A集合的点和B集合的点都描在二维坐标系中。我们可以很容易发现,A集合中x相同的尽量取y小的,B集合中x相同的尽量取y较大的。那么我们可以将集合A按照 x的递增且y的递增 进行排序。那么我们从前往后遍历y只有变小才有可能比前一个的答案更优。否则就没意义,不保留那个元素。
对于集合B来讲,我们也对B集合按照A集合的方式进行排序。因为我们需要尽量y大的数,我们可以从后往前遍历,只有y变大了才可能更优;处理完后我们得到的A,B集合都是x严格递增y严格递减的序列;
根据官方题解:
假设P代表一个点,\(i < j\),\(P_i , P_j\)是A集合的点,\(k < l\),\(P_k , P_l\)是B集合的元素,我们定义\(f\left ( x,y\right )\)代表y点为右上角,x为左下角这个对角线所构成的矩形面积。画图可验证一个结论:
\(f\left ( i,k\right )+f\left ( j,l \right )\geq f\left ( i,l\right )+f\left ( j,k \right )\)
这个结论告诉我们,A集合中每个点的最优的与B的连线不会有相交除了端点处。有了这个结论分治法就可以解。
不知道如何分治,看这里。分治,快排算法类似的,选A集合中间的位置,找到A的最优解位置,以这个位置将原来的集合分为两个。再解决。具体看代码一目连然。
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <string>
using namespace std;
typedef long long int LL;
#define MSET(a,b) memset(a,b,sizeof(a))
const int maxn=1e6+10;
struct Point
{
int x,y;
}A[maxn],B[maxn],T[maxn];
bool cmp(Point a,Point b) //x递增,y递增
{
if(a.x!=b.x) return a.x <b.x;
return a.y<b.y;
}
LL ans;
int dp[maxn];
void dfs(int l,int r,int L,int R)
{
if(l>r||A[r].y>=B[L].y||A[l].x>=B[R].x) return ;
int mid=(l+r)>>1,pos=0;
int s=lower_bound(dp+L,dp+R+1,A[mid].x)-dp;
LL mx=0;
for(int i=s;i<=R;i++)
{
if(B[i].y<=A[mid].y) break;
LL val=1ll*(B[i].y-A[mid].y)*(B[i].x-A[mid].x);
if(val>mx) mx=val,pos=i;
//if(val>ans) ans=val,pos=i;
}
ans=max(ans,mx);
if(pos) dfs(l,mid-1,L,pos),dfs(mid+1,r,pos,R);
else dfs(l,mid-1,L,R),dfs(mid+1,r,L,R);
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)+1)
{
for(int i=1;i<=n;i++) scanf("%d%d",&A[i].x,&A[i].y);
for(int i=1;i<=m;i++) scanf("%d%d",&T[i].x,&T[i].y);
sort(A+1,A+1+n,cmp);
sort(T+1,T+1+m,cmp);
int ia=1,ib=1;
A[1]=A[1],B[1]=T[m];
for(int i=2;i<=n;i++)
if(A[i].y<A[ia].y) A[++ia]=A[i];
for(int i=m-1;i>0;i--)
if(T[i].y>B[ib].y) B[++ib]=T[i];
reverse(B+1,B+1+ib);
for(int i=1;i<=ib;i++) dp[i]=B[i].x;
// for(int i=1;i<=ia;i++)
// printf(">>> %d %d\n",A[i].x,A[i].y);
// for(int i=1;i<=ib;i++)
// printf(">>> %d %d\n",B[i].x,B[i].y);
ans=0;
dfs(1,ia,1,ib);
printf("%lld\n",ans);
}
return 0;
}
--------- F - Posterize -------------
题目描述:
就是求那个给出的公式,只考虑红色0-255像素值,给出每个颜色的像素点个数,要求把这些像素点变为最多k种像素值。求\(\sum _{i=1}^{n} \underset{1\leqslant i\leqslant k}{min}\left ( r_i-v_j\right )^2\)。每个像素原来的颜色与现在和它绝对值最小的元素的差值平方求和
解题思路:
dp的思想,0-255,时间复杂度:\(O(n^3)\)都可以,所以我们设 dp[i][j] :表示颜色分布0-i,变为最多j种颜色的答案。fx[i][j]:表示将颜色区间[i,j] 的颜色变为同样的某一种颜色的最小值。那么转移方程\(dp(i,j)=\underset{0\leq k\leq i}{min}(dp(k,j-1)+f(k+1,i))\)
code :
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <set>
#include <map>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long LL;
const int INF = 2e9 + 1e8;
const int MOD = 1e9 + 7;
const double eps = 0.0000000001;
void fre()
{
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
}
#define MSET(a, b) memset(a, b, sizeof(a))
#define fr(i, a, n) for (int i = a; i < n; i++)
const int maxn = 300;
LL fx[maxn][maxn], dp[maxn][maxn];
int red[maxn];
void Min(LL &x, LL t)
{
if (t < x)
x = t;
}
int main()
{
int n, k;
scanf("%d%d", &n, &k);
MSET(dp, 0x7f);
MSET(fx, 0x7f);
for (int i = 1; i <= n; i++)
{
int r, p;
scanf("%d%d", &r, &p);
red[r] = p;
}
for (int color = 0; color <= 255; color++)
{
for (int l = 0; l <= 255; l++)
{
LL num = 0;
for (int r = l; r <= 255; r++)
{
num += 1ll * (r - color) * (r - color) * red[r];
Min(fx[l][r], num);
}
}
}
//处理出,f[l][r];代表区间 l,r;
for (int i = 0; i <= 255; i++)
dp[i][1] = fx[0][i];
for (int i = 0; i <= 255; i++)
for (int j = 2; j <= k; j++)
for (int t = 0; t < i; t++)
Min(dp[i][j], dp[t][j - 1] + fx[t + 1][i]);
cout << dp[255][k] << endl;
return 0;
}