2020 ICPC Universidad Nacional de Colombia Programming Contest 題解


2020 ICPC Universidad Nacional de Colombia Programming Contest 題解

M. Magic spells

題意:

一個模式串 \(T\) ,以及 \(n\) 個文本 \(s\)

輸出 \(T\) 包含 \(s\) 前綴的最長子序列

思路:

處理模式串,依次記錄所有字符出現的位置

在匹配文本串時,通過二分查找大於 模式串當前匹配位置后是否還有相同的字符,找到第一個出現的位置,並更新匹配位置

總復雜度為 \(O(log(len_t)*len_s)\)

K. Katastrophic sort

題意:

一個字符串由兩個部分構成 字母部分和數字部分

給出兩個字符串,比較字典序大小

其中數字部分的大小比較為真實數字的大小比較

如 "11" 表示 11 > "2" 表示 2

思路:

對字符串部分和數字部分分別處理一下即可

G. Great dinner

題意:

給定固定序列 \(n\) , 其中 \(m\) 個固定順序求出滿足該順序要求的排列數量

思路:

觀察可得規律 \(n! / 2^m\)

E. Enter to the best problem of this contest!

題意:

交互題

給出一個完全二叉樹,標號如下

image-20201004171708835

給定樹的深度為 \(n\) , 你可以詢問最多不超過29次

詢問一個節點 \(x\) , 將會得到需要猜出的節點與當前詢問節點的最短距離。

輸出 \(!x\) 表示猜出的節點

思路:

從根節點開始

詢問 1 , 得到需要猜出節點的距離(深度),為 0 則結束

當前所猜節點深度小於所需要猜出節點深度時,詢問左兒子。如果距離增大則在右子樹,否則在左子樹。到達猜出節點深度時,向同層的兄弟節點移動。

所猜次數 小於等於 \(log(n)\)

B. Baby name

題意

給出兩個串,取兩個串中的子串並按順序拼接為一個串,使得該串字典序最大

思路

首先如何找到一個串的字典序最大的字串?

記錄字符串中最大字母出現的位置,最大位置連續的只記錄第一個出現的位置(第一個位置字典序最大)

依次比較每個起始位置的字典序大小,復雜度 \(0(n)\) 常數有點大

code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6;
char s[maxn],s1[maxn];
int ms[maxn],ms1[maxn];
int m,m1,cur,cur1;
//處理最大字典序子串,依次比較,2n復雜度 
int func(int mss[],char ss[],int len,int cur){
	int ans = mss[0];
	for (int i = 1; i <= cur; ++i) {
		int tmp = mss[i];
		int a = ans;
		while (tmp < len && ss[a] == ss[tmp]) {
			a++;
			tmp++;
		}
		if (tmp < len && ss[a] < ss[tmp]) ans = mss[i];
	}
	return ans;
}
int main(){
	scanf("%s %s",s,s1);
	int len = strlen(s);
	int len1 = strlen(s1);
	
	ms[0] = ms1[0] = 0;
	m = cur = 0;
	//找到該串最大字母的位置 
	for(int i=1;i<len;i++){
		if(s[i] > s[m]){
			m = i;
			cur = 0;
			ms[cur] = i;
			//重復長度以首位置為起點,必然最大 
			while(i<len && s[i+1]==s[i]) ++i;
		}else if(s[i]==s[m]){
			ms[++cur] = i;
			while(i<len && s[i+1]==s[i]) ++i;
		}
	}
	m1 = cur1 = 0;
	
	for(int i=1;i<len1;i++){
		if(s1[i] > s1[m1]){
			m1 = i;
			cur1 = 0;
			ms1[cur1] = i;
			//重復長度以首位置為起點,必然最大 
			while(i<len1 && s1[i+1]==s1[i]) ++i;
		}else if(s1[i]==s1[m1]){
			ms1[++cur1] = i;
			while(i<len1 && s1[i+1]==s1[i]) ++i;
		}
	}
	
	int res1 = func(ms,s,len,cur);
	int res2 = func(ms1,s1,len1,cur1);
	printf("%c",s[res1]);
	res1 += 1;
	while(res1<len && s[res1] >= s1[res2])
		printf("%c",s[res1++]);
	for(int i=res2;i<len1;i++){
		printf("%c",s1[i]);
	}
	//puts("");

}

L. Lonely day

題意

給定一個 \(n\times m\) 的圖,途中的$ ’S’ \(為起點,\)E$ 為終點。每次移動只能橫向或縱向地移向最近的一個干凈的點 \('.'\) 或終點,\('X'\) 為臟的點。如果能到達終點,則輸出步數最少並且字典序最小的路徑(下移為D、左移為L、右移為R、上移為U)。否則輸出-1

思路

最短路徑 \(BFS\) , 按照字典序大小放到隊列中, 這樣第一個到達終點的即為字典序最小且步數最小的路徑

記錄前驅,從終點進行dfs遞推回起點

code

#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define _for(i,a,b) for(int i=a;i<=b;++i) 
#define _rep(i,a,b) for(int i=a;i>=b;--i)
const int maxn = 2e3+10;
int n,m;
int sx,sy;
int vis[maxn][maxn];
int pre_x[maxn][maxn];
int pre_y[maxn][maxn];
int dir[][2] = {{1, 0}, {0, -1}, {0, 1}, {-1, 0}};
char dirr[] = "DLRU";
char ans[maxn<<1];
int cnt =0; 
char G[maxn][maxn];
struct node
{
    int x,y;
};
bool check(int x ,int y){
    if(x < 1 || x > n || y < 1 || y > m ) return false;
    return true;
}
void dfs(int x,int y){
    if(x == sx && y== sy){
        cout<<cnt<<endl;
        _rep(i,cnt-1,0){
        cout<<ans[i];
		}
		return ;
    }
    int prex = pre_x[x][y];
    int prey = pre_y[x][y];
    if(prey == y){
        if(prex < x) ans[cnt++] = dirr[0];
        else ans[cnt++] = dirr[3];
    }else{
        if(prey < y) ans[cnt++] = dirr[2];
        else ans[cnt++] = dirr[1];
    }
    dfs(prex,prey);
}
void bfs(){
    queue<node> q;
    q.push(node{sx,sy});
    vis[sx][sy] = 1;
    while(!q.empty()){
        node u = q.front();
        q.pop();
        if(G[u.x][u.y] == 'E') {
            dfs(u.x,u.y); //回溯路徑
            return ;
        }
        _for(i,0,3){
            int nex = u.x + dir[i][0];
            int ney = u.y + dir[i][1];
            while(check(nex,ney)){
                if(vis[nex][ney]) break;
                if(G[nex][ney] == '.' || G[nex][ney] == 'E'){
                    vis[nex][ney] = 1;
                    pre_x[nex][ney] = u.x;//上一個點位置
                    pre_y[nex][ney] = u.y;
                    q.push(node{nex,ney});
                    break;
                }
                //沒找到 . 或者 e 之前一直移動直到移出邊界
                nex += dir[i][0], ney += dir[i][1];
            }
        }
	}
	cout<<-1<<endl;
}
int main(){
    IOS
    cin>>n>>m;
    _for(i,1,n){
        _for(j,1,m){
            cin>>G[i][j];
            if(G[i][j] == 'S') sx = i,sy = j;
        }
    }
    bfs();
    return 0;
}

A. Approach

題意

給定二維坐標軸上給出四個點\(A,B,C,D\) , 其中 \(AB\)\(CD\) 分別組成兩組線段。現有兩個點從線段\(AB\)\(CD\) 的起點,\(A\)\(C\) 以相同的速度同時出發到另一個端點。到達終點的點不再移動,直到兩個點均到達終點即結束。

求出兩點間的最短距離

思路

兩點間的距離是一個凹函數,所以采取典型的三分方法來進行縮小范圍。同時由於精度的問題,要注意三分次數。

由於兩個點有可能其中一個點停止時另一個點仍在移動,所以可以分為兩段時間來計算。

  1. 沒有點到達終點
  2. 第一個到達終點 - 所有到達終點

然后對於點所在下標,先使用tan2處理與 \(x\) 軸的夾角 \(\delta\) , 然后再使用 \(cos,sin\) 函數轉換 \(x,y\) 方向的移動距離

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> pll;
template <typename T>
inline void rd(T& x)
{
	ll tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const ll inf = 0x3f3f3f3f;
const ll mod = 1e9+7;
const ll N = 2e3 + 10;
const ll M=1e7+10;
const double eps=1e-8;
struct Point{
	double x,y;
	Point(){}
	Point(double x, double y):x(x),y(y){}
	Point operator+(const Point &B){ return Point(x+B.x,y+B.y);}
	Point operator-(const Point &B){ return Point(x-B.x,y-B.y);}
	Point operator*(double k){ return Point(x*k,y*k);}
	void input(){
		cin>>x>>y;
	}
}st1,ed1,st2,ed2;
struct Line{
	Point st,ed;
	double len,ang;
	void input(){
		st.input();ed.input();
		ang=atan2(ed.y-st.y,ed.x-st.x);
	}
}a,b;
double Distance(Point A,Point B){
	double ans=(A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y);
	return sqrt(ans);
}
double get_dis(double mid){
	Point p1=Point(a.st.x+min(mid,a.len)*cos(a.ang),a.st.y+min(mid,a.len)*sin(a.ang));
	Point p2=Point(b.st.x+min(mid,b.len)*cos(b.ang),b.st.y+min(mid,b.len)*sin(b.ang));
	double ans=Distance(p1,p2);
	return ans;
}
double solve(double l,double r){
	for(int i=1;i<=1e2;++i){
		double midl=(l*2+r)/3,midr=(l+r*2)/3;
		if(get_dis(l)<get_dis(r)){
			r=midr;
		}
		else{
			l=midl;
		}
	}
	double ans=get_dis(l);
	if(ans-get_dis(r)>eps){
        ans=get_dis(r);
	}
	return ans;
}
int main() { 
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
	a.input();b.input(); 
	double l=0,r=max(a.len=Distance(a.st,a.ed),b.len=Distance(b.st,b.ed));
	printf("%.12lf\n",min(solve(0,min(a.len,b.len)),solve(min(a.len,b.len),max(a.len,b.len))));
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM