UPC第三次随堂检测

UPC第三次随堂检测,第1张

第一题

题目要求两部分尽可能的相同,差值尽可能的小,这也就告诉我们,我们要尽可能的匹配sum/2,所以我们把sum/2作为总体积,对各个物体进行背包。


dp[sum/2]就是最接近一半的结果,我们用总和减去这个值,就得到了另一半。


【以下背包采用了01背包压维 *** 作】

#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

int v[1000],dp[50000*10];

int main()
{
    int t;
    cin>>t;

    while(t--)
    {
        int sum=0,n,half;

        memset(dp,0,sizeof(dp));


        if(n==1)
        {
            int x;
            cin>>x;

            cout<>v[i];

            sum+=v[i];
        }


         half=sum/2;


        for(int i=1; i<=n; i++)
        {
            for(int j=half; j>=v[i]; j--)
            {
                dp[j]=max(dp[j],dp[j-v[i]]+v[i]);
            }
        }

        cout<

第二题,是常见的优先队列贪心问题;

涉及知识点有,priority_queue,结构体重载运算符,贪心;

优先队列嵌套结构体时,有一个规律,我们想让某一个变量大的在上面,则我们结构体内部要写小于号
struct node
{
    int pos,sum;

    friend bool operator<(node a,node b)
    {
        return a.sum>b.sum;
    }
};

将问题微观化,假如数列就只有一个,那么显然,前k个最小和就是各个元素

n==2时,我们在一个数列基础上进行扩展,对第二个数列b进行排序,首先,b[1]相对于同数列其他元素来说,肯定最优,因此我们对第一次筛选出的k个元素全部加b1,也就是a1+b1,a2+b1....

把它们全部放入优先队列,这样我们头部先取第一个,再进行扩展 *** 作;第一个一定是a1+b1,我们直接把它作为第一小即可。


可是,如果我们直接这样遍历,我们的第二个元素(a2+b1)不一定是第二小,那么谁会有可能取代它呢? a3+b1?肯定不是,之后更不是!a3+b2?更差了,之后不用在考虑。


是 a1+b2!是a1+b3!是之后种种,我们不知道其大小关系,我们先把a1+b2压入队列,能在紧接着第二次循环“浮上来”,那么它一定是第二小的,否则,我们还有最优的来承接,如果真的浮上来,那么再压入下一个即可。


也就是ax+b1变成ax+b2再放进队列中。


意思大概是,既然ax+b1能成为最优的情况,我们不防把ax次优的情况,也加入队列。


如果加入之后还能"浮上来“,再进行这种 *** 作,如果不能浮上来,那就对第二个进行扩展,以此类推,进行k次,一定能筛选出前k个最小的。


以上 *** 作对应【普及-提高】洛谷橙色难度,本题是其扩展,应该能达到较容易的绿题难度。


那么我们既然在两队数列基础上筛选出了前k小,不防把这新的前k小看成新的数列,对第三队数列再进行以上 *** 作。


#include
#include
#include
#include
#include
using namespace std;

struct node
{
    int pos,sum;

    friend bool operator<(node a,node b)
    {
        return a.sum>b.sum;
    }
};

priority_queueq;

int val[1010][1010], bestsum[1010];


int main()
{


      int k;
      while(cin>>k)
      {
          for(int i=1;i<=k;i++)
          {
              for(int j=1;j<=k;j++)
              {
                  cin>>val[i][j];
              }

              sort(val[i]+1,val[i]+1+k);//排序

          }

          for(int i=1;i<=k;i++)
          {
             bestsum[i]=val[1][i];   //一个数列时前k小
          }



          for(int i=2;i<=k;i++)
          {
             while(!q.empty())  //清空,防止上一步影响
             {
                 q.pop();
             }


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

                  head.pos=1;

                  head.sum=bestsum[j]+val[i][1];  //"加b1"

                  q.push(head);//入队


              }
            
            
              //筛选前k小元素

              for(int j=1;j<=k;j++)
              {
                  struct node now=q.top();  //头部

                  bestsum[j]=now.sum;      //前小

                  q.pop();        //删头


                  struct node tail;

                  tail.pos=now.pos+1;    //扩展操作

                  if(tail.pos<=k)  //不要越界
                  {

                      tail.sum=now.sum-val[i][now.pos]+val[i][tail.pos];  //"去掉原来的b1,换成b2"

                      q.push(tail);   //别管能不能浮上来,先压进去
                  }

              }
              



          }

          for(int i=1;i<=k;i++)
          {
              cout<

第三题

设dp[i][j]为前i个唱片,且第i个播放j分钟时的最大歌曲数。


题目要求按照时间顺序来安排,那么我们直接顺序遍历即可。


对于每一首歌,只能放进一个唱片。


我们只需要在遍历一首歌时,枚举出放进各个唱片的情况即可。


枚举时,对于每一个唱片内部,我们倒序枚举时间,是为了防止重复放入一首歌。


对于每一个唱片,我们仍然倒序枚举,是为了防止一首歌放在了两个不同唱片。


一个新歌,可以仅仅放在唱片内部 f[m][j-t[i]]+1,也可以承接前i-1部唱片,利用当前时间全部放置这一首。


20 20 5
4 9 2 19 5 3 10 15 18 4 3 9 14 17 1 20 15 19 12 6

为例

放置第四首19时,第四部唱片之所以不是1而是4,是因为承接了前三部的放法,本首歌单独放在第四部唱片,其余歌曲全部放在前三唱片里面。


#include
#include
#include
#define maxn 21
using namespace std;
int N,T,M;
int f[110][110];
int t[110];
int max(int i,int j,int k)
{
    i=max(i,j);
    i=max(i,k);
    return i;
}
int main()
{
    cin>>N>>T>>M;
    for(int i=1; i<=N; i++)
    {
        cin>>t[i];
    }

    for(int i=1; i<=N; i++)
    {
        for(int m=M; m>=1; m--)
        {
            for(int j=T; j>=t[i]; j--)
            {
                f[m][j]=max(f[m][j],f[m-1][T]+1,f[m][j-t[i]]+1);
            }
        }

    }
    cout<

; 

第四题没读懂

第五题,压了一维

#include 
#include 
#include 
#include 
#include 
#include 

#include 

using namespace std;
int dp[100];

int main()
{

    dp[0]=1;

    for(int i=1; i<=50; i++)
    {
        for(int j=i; j<=50; j++)
        {
            dp[j]+=dp[j-i];

        }
    }

    int n;
    cin>>n;

    cout<

第六题,dp[i]代表前i个餐馆能获得的最大利益,状态转移当且仅当二者距离大于k.

#include 
 #include 
  #include 
  #include 
 #include 
  #include 

  #include 

using namespace std;

int dis[1010],w[1010];

int dp[1010];

int main()
 {


  int t;
  cin>>t;

  while(t--)
  {
      int n,k;
      cin>>n>>k;

      memset(dp,0,sizeof(dp));



      for(int i=1;i<=n;i++)
      {
         cin>>dis[i];

      }

      for(int i=1;i<=n;i++)
      {
          cin>>w[i];
      }


      for(int i=1;i<=n;i++)
      {
          dp[i]=w[i];

          for(int j=1;jk)
              {
                  dp[i]=max(dp[i],dp[j]+w[i]);

              }
          }
      }


      cout<

 第七题不适合用dp,可以采用深度优先搜索,枚举全部和为x的组合,而非排列,能大大减少时间,不会超时。


每凑成一次x,提取出全部元素,标记次数加1,一个数必须使用,当且仅当它和x出现的次数是相同的。


#include 
#include 
#include 
#include 
#include 
#include 

#include 

using namespace std;

int cnt[10000+10], temp[10000+10],timecnt=0,n, x, a[1000], book[1000];

void dfs(int step,int nowsum)
{
    if(nowsum>x)
    {
        return ;
    }

    if(step>n)
    {
        return ;
    }

    if(nowsum==x)
    {

        timecnt++;

        for(int i=1; i>n;

    cin>>x;


    for(int i=1; i<=n; i++)
    {
        cin>>a[i];
    }


    sort(a+1,a+1+n);

    dfs(1,0);


    int ans=0;

    for(int i=1; i<=n; i++)
    {
        if(cnt[a[i]]==timecnt)
        {
            ans++;
        }
    }

    if(ans==0)
    {
        cout<<0< 

第八题是二维费用01背包,两维度均需倒序枚举,能使数量增加就放入。


注意体力不能为0,也就是这一维容量只能是M-1,最后找到了dp[N][M-1]代表使用最大体力和能量球所能得到的最大数目,但要求体力最小,我们可以遍历整个dp数组,找到和dp[N][M-1]一样数量的结果,取消耗血量的最小值,用M减去,就是剩下的。


但遍历i=N这一层就够了,其余情况,都不如这一种情况更优。


#include
#include 
const int maxn=1010;
int N,M,K;
int n[maxn],m[maxn];  //储存消耗精灵球数量ni 和 体力值mi
int dp[maxn][maxn];   //动态规划数组

int main()
{
    scanf("%d%d%d",&N,&M,&K);
    for(int i=1; i<=K; i++)
    {
        scanf("%d%d",&n[i],&m[i]);
    }


    for(int i=1; i<=K; i++)
    {
        for(int j=N; j>=n[i]; j--)
        {
            for(int k=M-1; k>=m[i]; k--)
            {

                    if(dp[j-n[i]][k-m[i]]+1>dp[j][k])
                    {

                        dp[j][k]=dp[j-n[i]][k-m[i]]+1;
                    }
            }
        }
    }




    int temp=M;

    for(int i=0; i<=M-1; i++)
    {
        if(dp[N][M]==dp[N][i])
        {
            temp-=i;
            break;
        }
    }
    printf("%d %d\n",dp[N][M-1],temp);
    return 0;
}

第九题

#include
#include 
# include
# include

int dp1[100010],dp2[100010];
int a[100010];
using namespace std;
int main()
{



   int t;
   cin>>t;

   while(t--)
   {
       int n;
       cin>>n;

       //dp1代表前i填卖出时获得的最大利润,本次可以卖也可以不卖
       int min1=99999999;

       memset(dp1,0,sizeof(dp1));

       memset(dp2,0,sizeof(dp2));

       for(int i=1;i<=n;i++)
       {
           cin>>a[i];

           min1=min(min1,a[i]);

           dp1[i]=max(dp1[i-1],a[i]-min1);




       }
       //dp2代表i开始后几天买入的最大利润,
       int max1=-99999999;
       for(int i=n;i>=1;i--)
       {
           max1=max(max1,a[i]);

           dp2[i]=max(dp2[i+1],max1-a[i]);


       }

       int ans=-9999999999;

       for(int i=1;i<=n;i++)
       {
           ans=max(ans,dp1[i]+dp2[i]);

       }

       cout<

第十题,每一个位置往前推,当且仅当推移位置之间是回文时才能状态转移

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;


char ch[2000];
int len;
int f[2000];

bool check(int s, int e)
{

    bool flag = true;
    for (int i = s; i < s + (e - s + 1) / 2; i++)
    {
        if (ch[i] != ch[e - (i - s)])
        {
            flag = false;
            break;
        }
    }
    return flag;
}

int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        cin >> ch;
        len = strlen(ch);
        for (int i = 1; i < len; i++)
            f[i] = i;


        for (int i = 1; i < len; i++)
            for(int j=0; j<=i; j++)
            {
                if (check(j, i) && j == 0)
                {
                    f[i] = 0;
                    break;
                }
                if (check(j, i))
                    f[i] = min(f[i], f[j - 1] + 1);
            }
        cout<

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/563375.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-02
下一篇 2022-04-02

发表评论

登录后才能评论

评论列表(0条)