BAPC2017 Preliminaries (solve6/12)

题目链接:https://nanti.jisuanke.com/?kw=Benelux%20Algorithm%20Programming%20Contest%202017,%20Preliminaries

A题

题意:(对于英语差的我瑟瑟发抖的题意)就给你n个店铺和n个店铺卖的的东西,然后给你一个顺序,问你是否可以从0---n-1商店中递增序列选出来一些店铺可以买到到这些物品,如果购买方案唯一输出什么玩意,没有输出什么玩意,多种方案输出什么玩意。

思路:先将所有物品拍个编号,然后看看那个物品可以在那些店铺购买,然后从前往后匹配,找到满足当前物品的最小编号店铺,然后依次往后,如果找到一条这样的路径的话,在从后往前匹配,找到购买这个物品的最大的编号,然后依次往前找,最后判断2条路是否一样,不一样肯定多条,如果一样肯定单路径。值得一提的是,需要提前判断下购买的物品是否在你查询的商店中出现过,如果没有直接没可能。

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
map<string,int>M;   
vector<int>V[maxn]; //储存一个物品卖的所有商店 
int bian[maxn];     //对于某个物品转换成编号 
int qian[maxn];     //从前边的编号 
int hou[maxn];     //从后边的编号 
int cnt;          //物品排序的编号 
int getid(string s)//将物品编号化 
{
    if(!M.count(s))
        M[s]=cnt++;
    return M[s];
} 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n,k,m;
    cin>>n;
    cin>>k;
    for(int i=0;i<k;i++)
    {
        int num;
        string s;
        cin>>num>>s;
        int temp=getid(s);
        V[temp].push_back(num);//卖temp这个物品的商店有那几个 
    }
    for(int i=0;i<cnt;i++)//将店铺号排个序
        sort(V[i].begin(),V[i].end());
    int flag=1;
    cin>>m;
    for(int i=0;i<m;i++)
    {
        string s;
        cin>>s;
        if(!M.count(s))//出现没卖的物品 
        {
            flag=0;
            break;
        }
        bian[i]=M[s];//名字换算成编号 
    } 
    if(flag==0)
    {
        cout<<"impossible"<<endl;
        return 0;
    }
    int pre=-1;
    flag=1;
    for(int i=0;i<m;i++)//从前往后找一条路径 
    {
        int id=bian[i];//查询是那个物品 
        int temp=lower_bound(V[id].begin(),V[id].end(),pre)-V[id].begin();//找到第一个大于的位置
        if(temp<0 || temp>=V[id].size())//查无此 
        {
            flag=0;
            break;
        } 
        qian[i]=V[id][temp];//记录下是个店铺 
        pre=V[id][temp];//下次从这个店铺开始 一个店铺可以多买 
    } 
    if(flag==0)
    {
        cout<<"impossible"<<endl;
    }
    else//判断是否有多路径 
    {
        int pre=*V[bian[m-1]].rbegin();//最后一个物品的最后一个商店 
        for(int i=m-1;i>=0;i--)
        {
            int id=bian[i];
            int temp=upper_bound(V[id].begin(),V[id].end(),pre)-V[id].begin()-1;//找到一个刚好<=当前店铺 
            hou[i]=V[id][temp];
            pre=V[id][temp]; 
        } 
        int flag=1;
        for(int i=0;i<m;i++)//查询是否有多路径 
        {
            if(qian[i]!=hou[i])
                flag=0;
        }
        if(flag==0)
            cout<<"ambiguous"<<endl;
        else
            cout<<"unique"<<endl;
    }
    return 0;
}

B题

题意:给你h,w。问你可以构建多少不同的盆栽保证高度不超过h,然后边数不超过w.

思想:dp[i][j]代表高度不超过i j条边的方案数。但是去转移呢。

官方题解

递推方程为Dp[h][w]=sum( 1<=i<=w   dp[h-1][i]*dp[h][w-i])  因为dp[i][j]是类似前边的一个前缀和

代表分解左右子树,右子树保证高度够,然后枚举左子树的高度,保证高度最大为h-1,这样最高跟右边一样高。

最后需要dp[h][w]-dp[h-1][w]  因为只要高度为h的所以需要减去前边的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
ll dp[305][305]; 
int main()
{
	int h,w;
	scanf("%d%d",&h,&w);
	for(int i=1;i<=h;i++)//高度小于i的有一个棒的方案 
		dp[i][1]=1;
	for(int i=1;i<=h;i++)
	{
		for(int j=1;j<=w;j++)
		{
			for(int k=1;k<=j-1;k++)
			{
				dp[i][j]=(dp[i][j]+dp[i-1][k]*dp[i][j-k]%mod)%mod;
			} 
		}
	}
	ll ans=(dp[h][w]-dp[h-1][w]+mod)%mod;
	printf("%lld\n",ans);
	return 0;
} 

C题

题意:给你一个城市的n个点之间的图,然后让你找一条从0到n-1的最长单一路(保证唯一),然后问你删除最少的几条边可以满足题意,边号从小到大输出。

思路:Dijkstra算法记录最短路径,然后判断这条路的每个点除了通往最大值的那个边,其余的边标记下,都是需要删掉的,这样就保证了最少的边。

#include<bits/stdc++.h>
using namespace std;
struct node{
    int v;
    int cost;
    int next;
    int id;
}no[2050];
int head[2005];
int dist[2005];
int pre[2005];
int vis[2005];
int id[2005];//记录id 
int cnt;
void add(int u,int v,int cost,int id)
{
    no[cnt]={v,cost,head[u],id};
    head[u]=cnt++;
}
int main()
{
    memset(head,-1,sizeof(head));   
    cnt=0;
    int n,k;
    cin>>n>>k;
    for(int i=0;i<k;i++)
    {
        int u,v,cost;
        scanf("%d%d%d",&u,&v,&cost);
        add(u,v,cost,i);
        add(v,u,cost,i);
    }
    dist[0]=1<<30;
    pre[0]=-1;
    while(1)
    {
        int j=-1;
        int Max=0;
        for(int i=0;i<n;i++)
        {
            if(vis[i]==0 && Max<dist[i])
            {
                Max=dist[i];
                j=i;
            }
        }
        if(j==-1)
            break;
        vis[j]=1;
        for(int i=head[j];i!=-1;i=no[i].next)
        {
            if(vis[no[i].v]==0 && dist[no[i].v] < min(dist[j],no[i].cost))//判断两个那个小 
            {
                dist[no[i].v]=min(dist[j],no[i].cost);
                pre[no[i].v]=j; //标记来的点 
                id[no[i].v]=no[i].id;//标记边号 
            }
        }
    }
    set<int>S,S1;
    k=n-1;
    while(k!=0)//将这条路上的边存一下 
    { 
        S.insert(id[k]);
        k=pre[k];
    }
    k=n-1;
    while(k!=-1)
    {
        for(int i=head[k];i!=-1;i=no[i].next)//判断边是不是这条路的边 
        {
            if(!S.count(no[i].id))
                S1.insert(no[i].id);
        }
        k=pre[k];
    }
    int flag=1;
    set<int>::iterator it; 
    for(it=S1.begin(); it!=S1.end();it++)
    {
        if(flag==0)
            cout<<" ";
        cout<<(*it);
        flag=0;
    } 
    if(S1.size()==0)
        cout<<"none"; 
    cout<<endl;;
    return 0;
} 

D题

题意:刚开始有一个细菌,先让细菌分裂一个小时,然后做n次试验,每次试验需要ai个细菌,如果细菌不够输出error,否则最后输出剩多少细菌mod1e9+7

思想:java模拟过程即可,据说每次乘以2 会T,直接加本身或者左移都不会T,毕竟乘法器最后还需要转换成加法器,多了一个转换过程会变慢。

import java.math.BigInteger;
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        long t;
        t = cin.nextLong();
        int flag=1;
        BigInteger ans = BigInteger.ONE;
        BigInteger temp;
        for (int i = 0; i < t; i++) {
                temp=cin.nextBigInteger();
                ans=ans.add(ans);
                if(ans.compareTo(temp)<0)
                    flag = 0;
                else
                    ans=ans.subtract(temp);
        }
        if(flag==0)
            System.out.println("error");
        else
            System.out.println(ans.mod(BigInteger.valueOf(1000000000+7)));
    }
}

E题

题意:找到一个最小的大于n的数而且是由k个不同2的幂构成的数

思想:k个不同的2的幂的数构成,说明这个数的二进制中1的数量是k个,所以就先考虑n+1中有多少个1,如果>k,就不断让n+(n&(-n))这样减少1,这样会存在一种情况,就是1的个数可能从大于k变成小于k,还需要再次补上,比如(1111这样的会减少)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int getbit(ll n)
{
	int ans=0;
	while(n)
	{
		if(n&1)
			ans++;
		n=n>>1;
	} 
	return ans;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	ll n,k;
	cin>>n>>k;
	n++;
	ll temp=getbit(n);
	if(temp==k)
		cout<<n<<endl;
	else
	{
		while(getbit(n)<k)
		{
			for(int i=0;i<63;i++)
			{
				if(!((1ll<<i)&n))//当前是0
				{ 
					n=n|(1ll<<i);
					break;
				}
			}
		} 
		while(getbit(n)>k)
		{
			n=n+(n&(-n)); 
		} 
		while(getbit(n)<k)
		{
			for(int i=0;i<63;i++)
			{
				if(!((1ll<<i)&n))
				{
					n=n|(1ll<<i);
					break;
				}
			}
		}
		cout<<n<<endl;
	}
	return 0;
} 

H题

题意:阅读理解 亚马匹,就是一对情侣在1百万天内看电影,如果对于一部电影都喜欢那肯定去看,如果有一个人喜欢的话,那么看了这个下次必须看对方喜欢的电影。

思路:模拟下就好了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1000005;
int a[maxn];
int b[maxn];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n,m;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		int temp;
		cin>>temp;
		a[temp]=1;
	}
	cin>>m; 
	for(int i=0;i<m;i++)
	{
		int temp;
		cin>>temp;
		b[temp]=1;
	}
	int ans=0;
	int flag1=0,flag2=0;
	for(int i=0;i<maxn;i++)
	{
		if(a[i] && b[i])
		{
			flag1=0;
			flag2=0;
			ans++;
		}
		else if(a[i] && flag1==0)
		{
			flag2=0;
			flag1=1;
			ans++;
		}
		else if(b[i] && flag2==0)
		{
			flag1=0;
			flag2=1;
			ans++;
		}
	}
	cout<<ans<<endl;
	return 0;
} 

 

BAPC(Bayesian Age-Period-Cohort)预测模型是一种统计模型,它用于分析和预测在考虑年龄、时期和队列效应影响下的数据。这种模型特别适用于人口学、流行病学和市场营销等领域,其中数据受到这些时间相关因素的影响。BAPC模型通常通过贝叶斯方法来实现,利用贝叶斯推断来估计模型参数。 在R语言中,可以使用不同的包来实现BAPC模型,比如`apc`或`BAMP`。但是,R中并没有一个专门叫做BAPC的包。以下是一个简单的例子,演示如何使用`apc`包进行年龄-时期-队列模型的分析。请注意,这里仅提供了代码的一个基本框架,你可能需要根据自己的具体数据和研究目的进行适当的调整。 ```R # 安装并加载apc包 install.packages("apc") library(apc) # 假设你有一个名为data的数据框,其中包含以下列: # year: 年份 # age: 年龄 # count: 每一年每个年龄组的计数数据 # age.group: 年龄分组变量 # 使用apc包中的函数来拟合模型 # apc_fit 是模型拟合对象 apc_fit <- apc模型拟合函数(data$year, data$age, data$count) # 查看模型结果 summary(apc_fit) # 进行预测 # predict函数中的newdata需要包含你想要预测的年份、年龄和队列信息 predictions <- predict(apc_fit, newdata=你的新数据框) # 打印预测结果 print(predictions) ``` 请记住,上述代码仅为示例,并不代表实际可用的代码。在实际应用中,需要根据数据的具体情况和分析需求来选择和调整合适的统计模型和分析方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值