Week11-练习

Week11-练习

第一题

评析:其实就是个线性DP,包了一层壳,但是这输入是真变态。

#include<bits/stdc++.h>

using namespace std;

int n,f[105][105];

int main(){

    scanf("%d",&n);

    int ans=1e8;

    for(int i=1; i<=n; i++){

        int k; scanf("%d",&k);

        for(int j=1; j<=k; j++){

            int x,y;

            f[i][j]=1e8;

            while(scanf("%d",&x)){

                if(x==0) break;

                scanf("%d",&y);

                f[i][j]=min(f[i][j], f[i-1][x]+y);//线性DP模板

            }

        }

        if(i==n){

            for(int j=1; j<=k; j++)

                ans=min(ans,f[n][j]);//选取答案

        }

    }

    printf("%d",ans);

    return 0;

}

第二题

评析:计数DP,代码看似短,实际非常难想。

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

int n;

ll f[505][505];

int main(){

    f[0][0]=1;

    scanf("%d",&n);

    for(int i=0; i<=n; i++){

        for(int j=0; j<=i; j++){

            for(int k=j+1; i+k<=n; k++)

                f[i+k][k]+=f[i][j];//转移方程

        }

    }

    ll ans=0;

    for(int i=1; i<=n; i++)

        ans+=f[n][i];

    printf("%lld",ans-1);//注意题目条件,次数一定要大于1,所以在此处减1

    return 0;

}

第三题

评析:思路是砝码两边都可以放,所以可以加也可以减,我们正反向遍历两遍就可以解决这个问题。

#include<bits/stdc++.h>

using namespace std;

int n,a[105],f[100005],ans,sum;

int main()

{

   cin>>n;

   for (int i=1;i<=n;i++)

   {

       scanf("%d",&a[i]);

          sum+=a[i];   //先把总数算出来

   }

   f[0]=1;

   for (int i=1;i<=n;i++)

    for (int j=sum;j>=a[i];j--)

    {

           if (f[j-a[i]]==1 && f[j]!=1)

           {

                  ans++;

                  f[j]=1;

              }

       }

   for (int i=1;i<=n;i++)

    for (int j=1;j<=sum-a[i];j++)

    {

           if (f[j+a[i]]==1 && f[j]==0)

           {

                  ans++;

                  f[j]=1;

              }

       }

   printf("%d",ans);

   return 0;

}

第四题

评析:这题较简单,把点两两枚举一下,判断一下是否越界,取最大即可(事实上开始没注意到O2复杂度,直接就枚举两个点了,运气了)

#include<bits/stdc++.h>

using namespace std;

struct node

{

       int x,y;

}a[3005];

int n,ans,f[5005][5005];

int main()

{

       cin>>n;

       for (int i=1;i<=n;i++)

       {

              cin>>a[i].x>>a[i].y;

              f[a[i].x][a[i].y]=1;//标记

       }

       for (int i=1;i<=n;i++)

        for (int j=1;j<=n;j++)

        {

              int u=a[i].x-a[j].x;

              int v=a[i].y-a[j].y;

              if (a[i].x-v<1 || a[i].x-v>5000 || a[j].x-v<1 || a[j].x-v>5000 || a[i].y+u<1 || a[i].y+u>5000 || a[j].y+u<1 || a[j].y+u>5000) continue;//判断是否越界,其实还是先表示一下比较好,否则这行实在太长

              if (f[a[i].x-v][a[i].y+u]==1 && f[a[j].x-v][a[j].y+u]==1) ans=max(ans,u*u+v*v);//取最大

        }

        cout<<ans;

        return 0;

}

第五题

评析:Floyd+二分,想到了用Floyd,但没想到用二分实操,看了题解才会。

#include<bits/stdc++.h>

using namespace std;

long long n,q,ans=1e9,a[105][105],b[105][105],dis[105][105],down[105];

bool check(long long d)

{

       long long temp=0,op=d-n*(d/n);

       for(int i=1;i<=n;i++)

       {

              down[i]=d/n+bool(i<=op);

              dis[i][i]=0;

       }//初始化

    for(int i=1;i<=n;i++)

     for(int j=1;j<=n;j++)

        dis[i][j]=max(a[i][j]-down[i]-down[j],b[i][j]);

    for(int k=1;k<=n;k++)

     for(int i=1;i<=n;i++)

      for(int j=1;j<=n;j++)

        dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);//Floyd

    for(int i=1;i<=n;i++)

      for(int j=1;j<=n;j++)

        temp+=dis[i][j];

    if(temp<=q) return true;

    else return false;//判断

}

int main()

{

       cin>>n>>q;

       for (int i=1;i<=n;i++)

        for (int j=1;j<=n;j++)

          cin>>a[i][j];

       for (int i=1;i<=n;i++)

        for (int j=1;j<=n;j++)

          cin>>b[i][j];

    long long l=0,r=1e9,mid;

    while(l<r)

    {

           mid=(l+r)/2;

           if (check(mid))

           {

                  ans=min(ans,mid);

                  r=mid;

              }

              else l=mid+1;

       }//二分

       if (l==1e9) cout<<"-1"<<endl;

       else cout<<ans<<endl;

       return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值