2020训练赛5

A题

在这里插入图片描述

题目描述:
输入给出3个数x,y,n
判断从1——n的所有数字中,
如果n被x整除,输出Fizz
如果n被y整除,输出Buzz
如果n既被x整除又被y整除,输出FizzBuzz
否则输出n

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=1e5+10;
int x,y,n;
int main(){
    scanf("%d%d%d",&x,&y,&n);
    for (int i=1;i<=n;i++){
        if(i % x==0 && i % y==0) puts("FizzBuzz");
        else if(i % x==0) puts("Fizz");
        else if(i % y==0) puts("Buzz");
        else printf("%d\n",i);
    }
    return 0;
}

B题

在这里插入图片描述

题目描述:
现在有2个候选人和n个投票人,每个人只能投两个候选人的其中一个。
现在给你四个数,三整数一小数n,v1,v2,w
表示一共的投票者,已经投了第一个人的人数和投了第二个人的人数v1和v2,求是否最后能决定第一个候选者的胜出

1.剩余的人投完票后,保证获胜概率>=w%,输出GET A CRATE OF CHAMPAGNE FROM THE BASEMENT!
2.不可能获胜(v2已经获得了半票了),输出RECOUNT!
3.不确定,输出PATIENCE, EVERYONE!

C(m,n)×0.5m(选中)×0.5n-m(未选中)=C(m,n)×0.5n
则如果C(n-v1-v2,n/2+1-v1) × 0.5n-v1-v2>w %,
则一定获胜

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
int T;
int n,v1,v2;
double w;
ll Cnm(int n,int m)//计算组合数C(n,m) 
{
	if(m>n/2)//根据组合公式,可以减少枚举量 
	m=n-m;
	ll a=1,b=1;
	for(int i=1;i<=m;i++)//顺序进行m次运算 
	{
		a*=n+1-i;//计算前i项运算结果的分子a和分母 b 
		b*=i;
		if(a%b==0)
		{
			a/=b;
			b=1;
		}
	}
	return a/b;
}

int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d%lf",&n,&v1,&v2,&w);
        double sum=0;
        w/=100.0;
        for (int i=n/2+1-v1;i<=n-v1-v2;i++){
            sum+=1.0*Cnm(n-v1-v2,i)*pow(0.5,n-v1-v2);
        }
        if (v2 == n / 2 && n % 2 == 0) puts("RECOUNT!");
		else{
			if (sum>w)
                puts("GET A CRATE OF CHAMPAGNE FROM THE BASEMENT!");
			else if (v2 >= (n / 2 + 1)) puts("RECOUNT!");
			else puts("PATIENCE, EVERYONE!");
		}
    }
    return 0;
}

C题

在这里插入图片描述

题意:
有若干密码,每个密码都有一个称为正确密码的概率pi,现在问你从这串密码序列中找到正确密码的最小期望次数是多少

我们可以发现,对于密码序列,每次从剩下中选一个是C(n,1)=n的,n为当前还未选过的密码数,想要使得次数最少,则C(k,1)*pi每次要保证尽量小,因为C(k,1)是一个定值,于是我们将小的pi匹配大的C(k,1)就可以了,
于是答案为

∑ i = 1 n ( p i × C ( n + 1 − i , 1 ) ) ( p i 为 递 增 序 列 ) \sum_{i=1}^{n}{(p_i×C(n+1-i,1)) (pi为递增序列)}\\ i=1n(pi×C(n+1i,1))(pi)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
const int maxn=600;
double pi[maxn];
string s[maxn];
int main(){
    int n;
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        cin>>s[i];
        scanf("%lf",&pi[i]);
    }
    sort(pi+1,pi+n+1);
    double ans=0.0;
    for (int i=1;i<=n;i++){
        ans+=pi[i]*(n+1-i);
    }
    printf("%.4f\n",ans);
    return 0;
}


D题

在这里插入图片描述

题目描述:
有一个城市网络,有n个位置,m条街道,每两个位置之间可能有一条单向的街道,通过每一条街道需要一定的时间。有一家物流公司在0号节点,物流公司一天接到了C个委托人的物件,每个委托人在一个特定的位置。物流公司需要在规定时间送达,问至少需要多少辆车子才能把所有的物件在规定的时间送达。

输入图G(V,E)
计算所有的从仓库到委托人的最短路径
建立G’(V’,E’),V’包含起点和所有的委托人,且{<u,v>∈ V×V, d(0,u)+d(u,v)=d(0,v)}

G’是给一个DAG,于是答案是从0点开始的最小的不相交路径覆盖

解决方法:对题目给的图进行最大匹配,如最大匹配为ans,节点数量为n则最小路径为(n-ans)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>
#include <queue>
#define ll long long
#define P pair<ll,int>
using namespace std;
const ll inf = 1e15;
const int N = 2e3 + 10;
vector<P> G[N];
vector<int> GG[N];
ll dis[N][N];
int n,m,C;
int a[N];
void dij(ll dis[],int s){
    for(int i = 0;i < n;i++) dis[i] = inf;
    dis[s] = 0;
    priority_queue<P,vector<P>,greater<P> >q;
    q.push(P(0,s));
    while(!q.empty()){
        P cur = q.top();q.pop();
        int u = cur.second;
        if(dis[u] < cur.first) continue;
        for (int i=0;i<G[u].size();i++){
        	int v=G[u][i].second;
        	int w=G[u][i].first;
// auto now:G[u]
            if(w + dis[u] < dis[v]){
                dis[v] = dis[u] + w;
                q.push(P(dis[v],v));
            }
        }
    }
}
int match[1000];
int vis[1000];
bool dfs(int u){
    vis[u] = 1;
//	auto v:GG[u] 
    for(int i=0;i<GG[u].size();i++){
    	int v=GG[u][i];
        int w = match[v];
        if(w < 0 || !vis[w] && dfs(w)){
            match[v] = u;
            return true;
        }
    }
    return false;
}
int Maxmatch(){
    int ans = 0;
    memset(match, -1, sizeof(match));
    for(int i = 1;i <= C;i++){
       memset(vis,0,sizeof(vis));
       if(dfs(i)) ans++;
    }
    return ans;
}
int main(){
    scanf("%d%d%d",&n,&m,&C); 
    for(int i = 1;i <= C;i++) scanf("%d",&a[i]);
    for(int i = 0;i < m;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        G[u].push_back(P(w,v));
    }
    dij(dis[0],0);
    for(int i = 1;i <= C;i++){
        int u = a[i];
        dij(dis[u],u);
        for(int j = 1;j <= C;j++){
            if(a[j] != u && dis[0][u] + dis[u][a[j]] == dis[0][a[j]]) GG[i].push_back(j + C);
        }
    }
    printf("%d\n",C - Maxmatch());
    return 0;
}

H题

在这里插入图片描述
题目描述:
类似于2048的玩法,给你一串序列,问怎样往序列中加字母使得序列压缩之后最后可以变成一个数

做法:

Compress the numbers as much as you can.
Insert 2 next to each remaining 2.
Compress and repeat with 4’s.
Add 8’s at the end until the sum is a power of 2.
Hard part - keeping track of insertions relative to the original list.
Lists of size 1 are already done

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
string solve(){
    string s;
    cin>>s;
    if(s.length()==1 || s=="22") return s;
    for (int i=0,cnt=0;i<=s.length();i++){//s的长度动态变化
        if((i==s.length() || s[i]>'2') && cnt&2) s.insert(i,"2");
        else if((i==s.length() || s[i]>'4') && cnt&4) s.insert(i,"4");
        cnt += s[i]-'0';
    }

    int tot=0;
    for (int i=0;i<s.length();i++) tot+=s[i]-'0';
    while(tot & (tot -1)){
        tot+=8;
        s+="8";
    }
    return s;
}

int main(){
    int T;
    cin>>T;
    while(T--){
        cout<<solve()<<endl;
    }
    return 0;
}

I题

在这里插入图片描述

题目描述:
给你n个大写都不同的杯子,
"string a"表示给你的是以a为半径的一个杯子
"b string"表示给你的是以b为直径的一个杯子
现在问你最后根据半径由小到大的杯子序列是什么

直接模拟即可

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
using namespace std;
typedef long long ll;
int n;
struct node{
    string str;
    double r;
    bool operator <(const node &rhs)const{ return r<rhs.r;}
}e[30];

int main(){
    scanf("%d",&n);
    getchar();
    for (int i=1;i<=n;i++){
        char s[30];
        gets(s);
        int len=strlen(s);
        int k;
        for (int j=0;j<len;j++){
            if(s[j]==' '){
                k=j;break;
            }
        }
        if(s[0]>='0' && s[0]<='9'){
            double sum=0;
            int f=1;
            for (int j=k-1;j>=0;j--){
               	sum+=f*(s[j]-'0');
                f*=10;
            }
            e[i].r=sum/2;
            for (int j=k+1;j<len;j++){
                e[i].str+=s[j];
            }
        }
        else{
            for (int j=0;j<=k-1;j++){
                e[i].str+=s[j];
            }
            double sum=0;
            int f=1;
            for (int j=len-1;j>=k+1;j--){
                sum+=f*(s[j]-'0');
                f*=10;
            }
            e[i].r=sum;
        }
    }
    sort(e+1,e+n+1);
    for (int i=1;i<=n;i++){
        cout<<e[i].str<<endl;
    }
    return 0;
}

J题

在这里插入图片描述

题目描述:
给你一个字符串,你最终的任务是需要把他给显示出来。
你得到了一个类似于栈的东西
现在你可以做三件事(每次都算一次操作)
1.push,把一个字母放进栈顶
2.print,打印栈顶的字母
3.pop,将栈顶的字母弹出
问最少需要多少次操作你可以把这个字符串展示出来

做法:
显然,打印次数肯定为字符串的长度len次,
因此我们需要优化push和pop操作。
有这样一个方法:
对于每个字符,在我们打印完他之后,我们要么把他弹出要么推入一个新的字符在栈顶处,但显然这种方法太慢了。
所以我们考虑动态优化

我们设dp(i,j)表示最小的pop和push操作来打印出子串substr(i,j)

于是

在这里插入图片描述

最终的答案就是 len+dp(0,n-1)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
using namespace std;
const int maxn=210;
const int inf=0x3f3f3f3f;
int T;
int dp[maxn][maxn];
string s;

int solve(int i,int j){
    if(i>j) return 0;
    if(dp[i][j]==-1){
        dp[i][j]=1+solve(i,j-1);
        for (int k=i;k<j;k++){
            if(s[k]==s[j]) dp[i][j]=min(dp[i][j],solve(i,k)+solve(k+1,j-1));
        }
    }
    return dp[i][j];
}

int main(){
    int T;
    scanf("%d",&T);
    getchar();
    while(T--){
        memset(dp,-1,sizeof(dp));
        getline(cin,s);
        int n=s.length();
        int ans=n+2*solve(0,n-1);
        cout<<ans<<endl;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值