2019 ICPC NAC补题

题目链接(PDF):https://cs.baylor.edu/~hamerly/icpc/qualifier_2019/naq2019.pdf

A Circuit Math

题解:后缀表达式,从左到右遍历表达式的每个数字和符号,遇到数字就进栈,遇到符号就将处于栈顶两个数字出栈,进行运算,并将运算结果进栈,直至栈空。

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

int n;
int inp[30];
char ch;
stack<int> s;
string t;

int main(){
    cin >> n;
    for(int i = 0;i < n; i++){
        cin >> ch;
        if(ch=='T') inp[i] = 1;
        else inp[i] = 0;
    }
    getchar();
    getline(cin, t);
    stringstream ss(t);
    char tmp;
    while(ss>>tmp){
        if(tmp>='A' && tmp <= 'Z'){
            s.push(inp[tmp-'A']);
        }
        else{
            int a  =s.top(); s.pop();
            if(tmp=='*'){
                int b = s.top(); s.pop();
                s.push(a&b);
            }
            else if(tmp=='+'){
                int b = s.top(); s.pop();
                s.push(a|b);
            }
            else if(tmp=='-'){
                s.push(1^a);
            }
        } 
    }
    if(s.top()) cout << "T";
    else cout << "F" ; 
    // system("pause");
    return 0;
}

B Diagonal Cut

收获:若题干没有明确具体形式, 就按照最容易的形式计算,如本题,将每个小矩形按照正方形处理
题解:若当做正方形处理,则只需要看直线y = M x N \dfrac{Mx}{N} NMx ((0,0)->(N,M))
是否经过正方形中心点即可。
正方形的左下角的点设为(p,q) 则其中心的点为(p+1/2, q+1/2)
即是否满足q + 1 2 \dfrac{1}{2} 21 = M N \dfrac{M}{N} NM ∗ * (2p+1) 约分后得 2q+1 = m n \dfrac{m}{n} nm(2p + 1)
显然此时 m n互质 则只有两种情况
一. m n都是奇数
显然式子左边的必然是整数(奇数), 则式子右边也是整数(奇数 所以k也是奇数) 即(2p+1) = kn =》有多少个k符合题意
得 k = 2 p n \dfrac{2p}{n} n2p + 1 n \dfrac{1}{n} n1 < 2 N n \dfrac{2N}{n} n2N + 1 n \dfrac{1}{n} n1 且k为整数 所以k<=2 N n \dfrac{N}{n} nN=2gcd(N,M) 又因为k是奇数所以k有gcd(N,M)个
二. m n中有一个是偶数
n(2q+1) = m(2p + 1) 显然该情况下 式中只有m或n是偶数 其余都是奇数 所以等式不成立
综上所述 return m%2 && n%2 ?gcd(M,N) : 0;

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

typedef unsigned long long ull;
ull n, m;

ull gcd(ull a, ull b){
    return b==0?a:gcd(b, a%b); 
}

int main(){
    scanf("%lld%lld",&n,&m);
    ull d = gcd(n,m);
    n /= d, m /= d;
    if(n%2&&m%2) printf("%lld\n",d);
    else printf("0\n"); 
    //system("pause");
    return 0;
}

C Gerrymandering

题解:签到题,就是题干有点长,理解题意就做出来了。

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

int n, m, tmp;
int vote[1005][2]={0};
int a, u, v;

int main(){
    int sum = 0;
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for(int i = 0; i < n; i++){
        cin >> a >> u >> v;
        vote[a][0] += u;
        vote[a][1] += v;
        sum += u+v;
    }
    int suma = 0, sumb = 0;
    for(int i = 1; i <= m; i++){
        int x, y;
        if(vote[i][0] > vote[i][1]){
            x = vote[i][0] - (vote[i][0]+vote[i][1])/2-1;
            y = vote[i][1];
            cout << "A";
        }
        else {
            y = vote[i][1] - (vote[i][0]+vote[i][1])/2-1;
            x = vote[i][0];
            cout << "B";
        }
        cout << " " << x << " " << y << endl;
        suma += x;
        sumb += y;
    }
    // cout << sum << suma << sumb << endl;
    printf("%.10f\n", abs(suma-sumb)*1.0/(sum*1.0));
    // system("pause");
    return 0;
}

D Missing Numbers

题解:签到题,在读取输入的同时,输出第i与i-1个数之间的数。最后,判断一下n与最后一个数是否相等

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

int n;
int arr[205];
int cnt = 0;

int main(){
    cin >> n;
    arr[0] = 0;
    for(int i = 1 ; i <= n; i++){
        cin >> arr[i];
        for(int j = arr[i-1]+1; j < arr[i]; j++)
            cout << j <<  endl;
    }
    if(n==arr[n]) cout << "good job"  << endl;
    // system("pause");
    return 0;
}

G Research Productivity Index

题解:概率dp,枚举提交k篇卷子,并计算出提交k篇的期望(等于结果*概率)。
f(i,j)表示提交i份卷子,通过j份的函数值,由题干可得 f ( i , j ) = i i j f(i, j )=i^{\frac{i}{j}} f(i,j)=iji(j>0);f(i,j)=0(j=0)。
用dp[i][j]表示提交i份卷子,通过j份的概率。并且从n份卷子选出k份卷子,显然当选出概率前k大的卷子时概率最大。
d p [ i ] [ j ] = p i ∗ d p [ i − 1 ] [ j − 1 ] + ( 1 − p i ) ∗ d p [ i − 1 ] [ j ] dp[i][j]=p_{i}*dp[i-1][j-1]+(1-p_{i})*dp[i-1][j] dp[i][j]=pidp[i1][j1]+(1pi)dp[i1][j]。则提交k份卷子的期望就等于 ∑ 0 k \sum_0^k 0kf(k,j)*dp[k][j]。提交1-n份卷子的期望最大值即为答案。

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

int n, tmp;
double inp[105];
double dp[105][105];

bool cmp(double a, double b){
    return a>b;
}

int main(){
    ios::sync_with_stdio(false);
    cin >> n;
    double ans = 0;
    for(int i = 1; i <= n; i++){
        cin >> tmp;
        inp[i] = tmp*1.0/100;
    }
    sort(inp+1, inp+n+1, cmp);
    //计算提交i份卷子,通过j份的概率
    dp[0][0] = 1;
    for(int i = 1; i <= n; i++){
        dp[i][0] = dp[i-1][0]*(1-inp[i]);
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            dp[i][j] = dp[i-1][j-1]*inp[i] + dp[i-1][j]*(1-inp[i]);
        }
    }
    //计算提交k份卷子的期望
    for(int i = 1; i <= n; i++){
        double res = 0;
        for(int j = 1; j <= i; j++){
            res += dp[i][j]*pow(j, j*1.0/(i*1.0));
        }
        ans = max(ans, res);
    }
    printf("%.9f\n", ans);
    // system("pause");
    return 0;
}

I Slow Leak

收获:弗洛伊德算法 这篇博客写的挺好的
用二维数组储存节点之间的距离,先将数组初始化((i,i)初始化0,其余初始化为∞)
然后根据输入 将(i,j)的值改为 输入的长度值k 即为任意两点之间的距离(无中转点)
核心算法:将1-n节点分别为中转点 改变两点之间的最短距离

for(int k = 1; k <= n; k++){//枚举中转点
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= n; j++){
				if(e[i][j] > e[i][k] + e[k][j]){
					e[i][j] =s e[i][k] + e[k][j];
				}
			}
		}
	}

题解:先用弗洛伊德算法 在完整图中计算出任意两点之间的最短距离
再建立一个只有起点、终点、修车店的图G’,将包含非G’节点的以及不符合题意的路径长度设为∞
再用一遍弗洛伊德算法算出起始点和终点满足题意的最短距离

#include<bits/stdc++.h>
using namespace std;
#define INF 1e18
typedef long long ll;

int n,m,t,d;
int rep[505];
ll table[505][505];//用于储存节点之间的距离

bool judge(int x){//判断是否为新图的节点 即起始点 修车店
    return x==1 || x==n || rep[x];
}

void floid(){
    for(int k = 1; k <= n; k++){
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                table[i][j] = min(table[i][j],table[i][k] + table[k][j]);
            }
        }
    }
}

int main(){
    scanf("%d%d%d%d",&n,&m,&t,&d);
    for(int i = 1; i <= t; i++){
        int x; scanf("%d",&x);
        rep[x] = 1;//修车店的节点序号
    }
    //初始化
    for(int i = 1; i <=n; i++){
        for(int j = 1; j <= n; j++){
            table[i][j] = INF;
        }
    }
    int u, v, l;
    for(int i = 1; i <= m; i++){
        scanf("%d%d%d",&u,&v,&l);
        table[u][v] = l;
        table[v][u] = l;
    }
    //弗洛伊达算法计算出 任意两点之间的最短距离
    floid();
    
    //新图只包含起始点和修车店 即人要么在起点 要么在修车店
    for(int i = 1; i <= n; i++){//将所有不满足题意的路径 设为无限大
        for(int j =  1; j <= n; j++){
            if(i!=j){
                if(judge(i)&&judge(j)){//是新图节点
                    if(table[i][j]>d) table[i][j] = INF;//但是到不了
                    continue;
                }
            }
            table[i][j] = INF;// i j不全是新图结点
        }
    }
    //再用一遍弗洛伊达算法
    floid();
    //cout << table[2][n] << endl;
    if(table[1][n]==INF) printf("stuck\n");
    else printf("%d\n",table[1][n]);
    //system("pause");
    return 0;
}

J Stop Counting!

题解:答案要么来自前缀和,要么来自后缀和。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
int n, m;
double inp[maxn], pre[maxn]={0}, suf[maxn]={0};

int main(){
    double ans = 0;
    ios::sync_with_stdio(false);
    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> inp[i];
        pre[i] = pre[i-1] + inp[i];
        ans = max(pre[i]/(i*1.0), ans);
    }
    for(int i=n; i>=1 ;i--){
        suf[i] = suf[i+1] + inp[i];
        ans = max(suf[i]/((n-i+1)*1.0), ans);
    }
    printf("%.9f\n", ans);
    // system("pause");
    return 0;
}

K Summer Trip

题解:暴力,不断枚举起点和终点,若起点和终点相同则结束枚举。虽然看着好像是O(n²),但实际上只有O(26*n)。对于每一次枚举,该串中字母均只出现过一次则count++

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

int n;
int vis[30];
string t;
int ans  = 0;

int main(){
    cin >> t;
    for(int i = 0; i < t.length()-1; i++){//枚举起点
        memset(vis, 0, sizeof(vis));
        for(int j = i+1; j<t.length(); j++){//枚举终点
            if(t[j]==t[i]) break;
            if(!vis[t[j]-'a']){
                vis[t[j]-'a'] = 1;
                ans++;
            }
        }
    }
    cout << ans << endl;
    // system("pause");
    return 0;
}

M Zipline

在这里插入图片描述

收获:开平方注意原值的正负; 出现除法 要考虑分母为0的情况(因为这个WA了)
题解:
初始化 g-=r, h-=r,看作是在地面上
最小值无可非议,两点间直线最短,题干中也明确提到是刚性绳(大概),所以不需要考虑松弛拉伸的情况。
最大值有两种求法
法一:对称 orz
任取一根柱子作关于ow的对称点P,连接另一个柱子的顶点形成直线l,l的长度就是最长距离,具体证明我忘了(小学知识orz)

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

typedef unsigned long long ull;

int main(){
    int t; cin>>t;
    while(t--){
        double w, g, r, h;
        cin >> w>>g>>h>>r;
        //if(g<h) swap(g,h);
        g -= r, h -= r;
        double mi = sqrt((g-h)*(g-h)+w*w);
        double mx = sqrt((g+h)*(g+h)+w*w);
        printf("%.8lf %.8lf\n",mi,mx);
    }

	return 0;
}

法二:求导
我当时的做法,设结点距离左柱子的距离为x,则最大值函数为
f(x) = g 2 + w 2 \sqrt{g^2+w^2} g2+w2 + h 2 + ( w − x ) 2 \sqrt{h^2 + (w-x)^2} h2+(wx)2 ,只需求导等于零求出x即可得到答案(就不求二阶导严格证明了)
f’(x) = x g 2 + w 2 \dfrac{x}{\sqrt{g^{2}+w^{2}}} g2+w2 x + x − w h 2 + ( w − x ) 2 \dfrac{x-w}{\sqrt{h^2+(w-x)^2}} h2+(wx)2 xw
导数等于0移项化简开平方可得 x = g w g + h \dfrac{gw}{g+h} g+hgw(分母不为0!!!) 再将x代入最大值函数即可求出答案

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

typedef unsigned long long ull;

int main(){
    int t; cin>>t;
    while(t--){
        double w, g, r, h;
        cin >> w>>g>>h>>r;
        if(g<h) swap(g,h);
        g -= r, h -= r;
        if(g==0&&h==0) printf("%.8lf %.8lf\n",w,w);
        else{
            double x = g*w/(g+h); // g x h w-x
            double mi = sqrt((g-h)*(g-h)+w*w);
            double mx = sqrt(g*g+x*x)+sqrt(h*h+(w-x)*(w-x));
            printf("%.8lf %.8lf\n",mi,mx);
        }
    }
    //system("pause");
	return 0;
}

剩下的题 之后再补吧 orz

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值