NEUQ-ACM week11

T1.P1796 汤姆斯的天堂梦

思路:

一开始以为是图论的最短路径,想了想发现自己不会dij,寻思找找别的方法,后来发现每一步需要关心的只有两个相邻级别的星球,想到可能可以用dp做这道题的状态转移方程不难找,用i表示星球的等级,j表示星球编号,c为代价,可得f[i][j]=min(f[i - 1][j] + c,f[i][j]),然后就是一些简单的数据初始化了

代码:

#include<bits/stdc++.h>
using namespace std;
int n,a,b,c;
int dp[105][105];
int main()
{
    cin>>n;//n级
    for(int i=1;i<=n;i++)
    {
        cin>>a;//i级星球个数
        for(int j=1;j<=a;j++)//枚举i级每个星球
        {
            dp[i][j]=0x3f3f3f3f;
            cin>>b;//i-1级的b星球
            while(b!=0)
            {
                cin>>c;//代价
                dp[i][j]=min(dp[i-1][b]+c,dp[i][j]);//找i-1级的b号星球到i级j号星球的最小代价
                cin>>b;                            //其实就是判定哪个c最小
            }
        }
    }
    int ans=INT_MAX;
    for(int i=1;i<=a;i++)
        ans=min(dp[n][i],ans);//判断到n级哪个星球花费最小
    cout<<ans;
        
}
//用i表示星球的等级,j表示星球编号,c为代价,
//可得dp[i][j]=min(dp[i - 1][j] + c,dp[i][j])

T2.P1806 跑步

思路:

看起来就像dp,一开始就往这方面想了。再瞅瞅,这似乎就是一个01背包嘛

然后我们来分析一下题意:题上说每次跑的圈数都必须比上一次多,往平时做的背包计数问题方面去想的话就可以用一维数组的01背包方法去写,只是每次转移的时候不是去最大值而是直接加上下一个状态的方法数。找出状态转移方程如下:

dp[i]:跑i圈的方案数量

转移方程: dp[j] += dp[j-i]

代码:

#include<bits/stdc++.h>
using namespace std;
long long dp[505];
int main()
{
    int n;
    cin>>n;
    dp[0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=n;j>=i;j--)//01背包一维优化
            dp[j]+=dp[j-i];//好吧我不知道为啥QWQ
    }
    cout<<dp[n]-1;//扣掉dp[0]
}

T3.P8742 [蓝桥杯 2021 省 AB] 砝码称重

思路:

也是用dp解

状态表示f[i][j]:表示前i个砝码中选,称出的重量为j的状态,为1表示可以称出,为0则不能称出。

分两种情况:

1.a[i] == j,那么f[i][j]一定可以被称出

2.a[i] != j,那么就可以从f[i - 1][j],f[i - 1][abs(j - a[i])](本质为f[i - 1][j - a[i]],为了防止出现负的下标,所以取绝对值),f[i - 1][j + a[i]]这3种情况中转移过来。

f[i - 1][j]:i - 1个砝码能称出的,i个也能称出

f[i - 1][j - a[i]]:加上当前的砝码

f[i - 1][j + a[i]]:减去当前的砝码

满足3种的任意一种,即可判断f[i][j]的重量为j的能否被称出

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,a[N],dp[105][N];//a[i]->砝码 
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
        if(!i)//考虑i=0(就是放第一个砝码的情况)
        {
            dp[i][a[i]]=1;
            dp[i][0]=1;
        }
        else
        {
            for(int j=0;j<N;j++)
                if(dp[i-1][j])//如果上一个状态的那些砝码能称出j重量的话
                {
                    dp[i][j]=1;//不放第i个
                    dp[i][j+a[i]]=1;//把第i个放右边
                    dp[i][abs(j-a[i])]=1;//把第i个放左边
                }
        }    
    }
    int ans=0;
    for(int i=1;i<N;i++)
        ans+=dp[n-1][i];//dp[n-1][i]表示用这n个砝码能否称出i重量(能的话就+1)
    cout<<ans;    
}

T4.P1959 遗址

思路(自己没写出来..参考题解大佬解法):

O(n2) 意味着枚举两个点,我们来观察一下这两个点与另外两个点的位置关系。

如图所示,我们将左上顶点定为 ii,左下顶点对应 jj,那么可以发现如下关系:

  1. 标红段为 ii 与 jj 的横坐标差,正好为左下顶点与右下顶点的纵坐标之差。

  1. 标蓝段为 ii 与 jj 的纵坐标差,正好为左下顶点与右下顶点的横坐标之差。

如此,我们即可仅仅通过两个点推出其余两个点的坐标。

代码:

#include<bits/stdc++.h>
using namespace std;
int n;
int x[3005],y[3005];
bool judge[3005][3005];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>x[i]>>y[i];
        judge[x[i]][y[i]]=true;
    }
    int ans=0;    
    for(int i=1;i<=n;i++)//思想:由两个点坐标推断出剩下两个点坐标
    {
        for(int j=1;j<=n;j++)
        {
            int a=x[i]-x[j];
            int b=y[i]-y[j];
            int Ax=x[i]-b;//以下是推算其他两个顶点的坐标
            int Bx=x[j]-b;
            int Ay=y[i]+a;
            int By=y[j]+a;
            if(Ax<1||Ax>5000||Bx<1||Bx>5000||Ay<1||Ay>5000||By<1||By>5000)continue;//越界就pass
            if(judge[Ax][Ay]&&judge[Bx][By])//如果推算出来这俩坐标确实存在
                ans=max(ans,a*a+b*b);
        }
    }
    cout<<ans;
}

T5.P8794 [蓝桥杯 2022 国 A] 环境治理

思路:

好长的题目...

但题目说得挺明显,多源最短路径,用Floyd

但是这题还需要考虑灰尘度的变化,说明图是动态改变的

所以没办法floyd一次直接出答案,因为Floyd得到的最短路径是看不出具体通过哪些点了,也就没办法体现灰尘度变化

然后看看题目:求最少需要几天,而天数越多越可能达标--想到用二分法猜答案

所以大概就是用二分法猜答案,再用floyd验证

主要就是先算出每一天每个城市的灰尘度,相当于每天要重新建边,再用floyd

代码:

#include<bits/stdc++.h>
using namespace std;
int mp[105][105];//存图
int lim[105][105];//最小灰尘度
int dust[105][105];
int ans=-1,n;
long long q;
bool check(int day)
{
    for(int i=1;i<=n;i++)//算这一天每个城市的灰尘度
        for(int j=1;j<=n;j++)
            dust[i][j]=max(mp[i][j]-day/n-(day%n>=i?1:0),lim[i][j]);
    for(int k=1;k<=n;k++)//floyd
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                dust[i][j]=min(dust[i][j],dust[i][k]+dust[k][j]);
    long long sum=0;
    for(int i=1;i<=n;i++)//算P
        for(int j=1;j<=n;j++)
            sum+=dust[i][j];
    if(sum>=0&&sum<=q)//判断是否符合要求
    {
        ans=day;
        return true;
    }
    else return false;
}
void search()//二分搜答案
{
    int l=0,r=0x3f3f3f3f;//为啥l=-1不行呀....
    while(l<=r)         //r=INT_MAX会TLE..QWQ
    {
        int mid=(l+r)/2;
        if(check(mid)) r=mid-1;
        else l=mid+1;
    }
}
int main()
{
    cin>>n>>q;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>mp[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>lim[i][j];
    search();
    cout<<ans;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值