“华为杯”中国矿业大学程序设计学科竞赛

18 篇文章 0 订阅
9 篇文章 0 订阅

G、毕业生的纪念礼物

theme:输入n表示纪念品数目,接下来n个数代表第i个数的类别,给每个人发3个不同类别的纪念品,问最多能给几个人发?1≤n≤100000,1≤r[i]≤1e9

solution:首先由贪心,每次应该选择数量最多的三个类别,这样就可以保证没一状态下都是总种类最多状态。注意选出三个后要重排,所以我们用优先队列存,每次取出三个-1,若值不为0,则再存入优先队列。

#include<bits/stdc++.h>
using namespace std;
#define far(i,t,n) for(int i=t;i<n;++i)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;

map<int,int>m;
map<int,int>::iterator it;
priority_queue<int>q;

int main()
{
    int n;
    cin>>n;
    far(i,0,n)
    {
        int x;
        scanf("%d",&x);
        m[x]++;
    }
    int ans=0;
    for(it=m.begin();it!=m.end();++it)
        q.push(it->second);

    while(q.size()>=3)
    {
        ++ans;
        int x1=q.top()-1;
        q.pop();
        int x2=q.top()-1;
        q.pop();
        int x3=q.top()-1;
        q.pop();
        if(x1)q.push(x1);
        if(x2)q.push(x2);
        if(x3)q.push(x3);
    }
    cout<<ans<<"\n";
}

J、你的粪坑v2

theme:给定n*n的矩阵地图,每个位置上有a[i]的shi.开始时位于(1,1),出口在(n,n)。每次可以选择向下或向右移动,问最少碰到多少shi能到达出口。其中没到达一个位置会碰到该位置上的shi,开始时可任意选择一个方向,之后每改变一个方向,会累积地依次受到1,2,4,8,16...的shi攻击。

solution:用dp[i][j][k][d]表示通过方向d,共改变k次方向,到达位置(i,j)所需最小代价。其中d=0表示从左边过来,d=1表示从右边过来。由于对每个位置要么从左要么从右边来,则对每一个(i,j)循环k的递推式为

dp[i][j][k][0] = min( dp[i][j-1][k][0] , dp[i-1][j][k-1][1] + (1<<(k-1)) ) + a[i][j]

dp[i][j][k][1] = min( dp[i-1][j][k][1] , dp[i][j-1][k-1][0] + (1<<(k-1)) ) + a[i][j]

最终遍历k找到最小值即可:

k :0->15  : ans = min( ans , min( dp[n][n][k][0] , dp[n][n][k][1] ) )

#include<bits/stdc++.h>
using namespace std;
#define far(i,t,n) for(int i=t;i<n;++i)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;

int a[200][200];
int dp[200][200][20][2];//最后一维为0:L,1:R

void getDp(int n)
{
    dp[1][1][0][1]=dp[1][1][0][0]=a[1][1];
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
        {
            if(i==1&&j==1)
                continue;
            dp[i][j][0][0]=dp[i][j-1][0][0]+a[i][j];
            dp[i][j][0][1]=dp[i-1][j][0][1]+a[i][j];
            for(int k=1;k<=15;++k)
            {
                dp[i][j][k][0]=min(dp[i][j-1][k][0],dp[i-1][j][k-1][1]+(1<<(k-1)))+a[i][j];
                dp[i][j][k][1]=min(dp[i-1][j][k][1],dp[i][j-1][k-1][0]+(1<<(k-1)))+a[i][j];
            }
        }
}

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        scanf("%d",&n);
        far(i,1,n+1)
            far(j,1,n+1)
                scanf("%d",&a[i][j]);
        memset(dp,0x3f,sizeof(dp));
        getDp(n);
        int ans=0x3f3f3f3f;
        for(int i=0;i<=15;++i)
            ans=min(ans,min(dp[n][n][i][0],dp[n][n][i][1]));
        printf("%d\n",ans);
    }
}

D、B题

theme:给定含有n个节点,n条有向边即将这条有向边反向的代价,问最少需要多少代价可以将这个图变成强连通图。所有值都<=100

solution:强连通图表示只一个强连通分量,所以不用tarjan,直接dfs即可。又n个点n条边为环,则每个节点与两个其它节点相连,由贪心,若从第一个节点出发按环顺时针遍历,若可以到达,则无需代价直接到达下一个节点,若不能到达,则加上代价反向到达。再次遍历到起始节点结束。还有一种情况就是反向逆时针遍历代价最小,可以直接用总的代价-正向代价。

#include<bits/stdc++.h>
using namespace std;
#define far(i,t,n) for(int i=t;i<n;++i)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;

int a[110],b[110],c[110],cost=0,n;

void dfs(int v,int idx)
{
    if(v==a[0])
        return;

    far(i,0,n)
        if(idx!=i)
        {
            if(a[i]==v)
                dfs(b[i],i);
            else if(b[i]==v)
            {
                cost+=c[i];
                dfs(a[i],i);
            }
        }
}

int main()
{
    while(~scanf("%d",&n))
    {
        int sum=0;
        cost=0;
        far(i,0,n)
            scanf("%d%d%d",&a[i],&b[i],&c[i]),sum+=c[i];
        dfs(b[0],0);
        printf("%d\n",min(cost,sum-cost));
    }
}

H、毕业生的序列游戏

theme:给定n

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值