zoukankan      html  css  js  c++  java
  • Topcoder SRM 605 div1 题解

    日常打卡~

    Easy(250pts):

    题目大意:你有n种汉堡包(统统吃掉~),每一种汉堡包有一个type值和一个taste值,你现在要吃掉若干个汉堡包,使得它们taste的总和*(不同的type值的个数)乘积越大,输出这个最大值。数据满足n<=50,type<=100,abs(taste)<=100000。

    这题好像是个贪心,才不是呢啊哼~

    首先我们发现,如果若干个汉堡包有同一个type值,那么我们可以把这一些汉堡包看成一个新的大汉堡包,它的type就是原来的type,它的taste就是取原先至少一个的最大taste之和,显然这一步是可以O(n)预处理完成的。

    然后由于type不是很大,我们可以枚举我们选取了多少个type,

    如果选择了k个type,那么一定是选择了taste最大的k个,然后乘以下,扫一遍k就好了。

    时间复杂度O(n^2),代码如下:

     1 #include <bits/stdc++.h>
     2 #define Maxn 107
     3 #define inf 10000007
     4 using namespace std;
     5 int f[Maxn],g[Maxn];
     6 int cnt1,cnt2,n;
     7 class AlienAndHamburgers
     8 {
     9     public:
    10     int getNumber(vector <int> type, vector <int> taste)
    11     {
    12         n=type.size();
    13         for (int i=1;i<=100;i++)
    14             f[i]=-inf;
    15         for (int i=0;i<n;i++)
    16             if (f[type[i]]<0) f[type[i]]=max(f[type[i]],taste[i]);
    17             else if (taste[i]>0) f[type[i]]+=taste[i];
    18         sort(f+1,f+100+1);
    19         memset(g,0,sizeof(g));
    20         g[100]=f[100];
    21         for (int i=100-1;i;i--)
    22             g[i]=f[i]+g[i+1];
    23 //g[100+1-i] now means the most taste[i] can get to choose i types of food
    24         int ans=0;
    25         for (int i=1;i<=100;i++)
    26             if (f[100+1-i]>-inf&&g[100+1-i]>0) ans=max(ans,g[100+1-i]*i);
    27         return ans;
    28     }
    29 };

    Medium(450pts):

    题目大意:有2n个数1~2n,现在把它们分成两个集合A和B,满足A和B都有n个元素,且A中第i小的元素和B中第i小的元素的差的绝对值至少为K,求方案数,数据满足n<=50,K<=10。

    我实在没有搞懂,为什么这题450分。。。

    比较显然是个dp吧,我们来考虑怎么来描述一个状态,

    在每一个状态下,都有两种数,第一种是已经被匹配的了,第二种是还没有被匹配的,

    而没有被匹配的也有两种,

    我们从大到小进行匹配,假设当前进行匹配的数为i,那么未被匹配的数一共有两种,

    第一种是大于等于i+K的数,这些数可以随意被匹配,

    第二种是i+1~i+K-1这些数,这些数不能和i匹配,

    这两种数中,第一种数我们只关心它的个数,第二种则要关心位置,需要用2^k种状态描述出来,

    然后直接转移就可以了,

    时间复杂度O(n^2*2^K),代码如下:

     1 #include <bits/stdc++.h>
     2 #define modp 1000000007
     3 #define Maxn 107
     4 #define Maxk 10
     5 using namespace std;
     6 int f[Maxn][Maxn][1<<Maxk];
     7 bool vis[Maxn][Maxn][1<<Maxk];
     8 int n,k;
     9 class AlienAndSetDiv1
    10 {
    11     int tryit(int i, int j, int t)
    12     {
    13 //i means how many numbers left now
    14 //j means how many numbers in set A can be free-matched
    15 //t means the condition of the numbers in set A that cannot be free-matched
    16         if (vis[i][j][t]) return f[i][j][t];
    17         vis[i][j][t]=true;
    18         int res=0;
    19         if (i==0)
    20         {
    21             if (j==0&&t==0) res=1;
    22             f[i][j][t]=res;
    23             return res;
    24         }
    25         if (j==0&&t==0)
    26         {
    27 //all the i numbers are matched now
    28             if (k==1) res=tryit(i-1,1,0); else res=tryit(i-1,0,1);
    29             res=(2LL*res)%modp;
    30             f[i][j][t]=res;
    31             return res;
    32         }
    33         if (j>0)
    34         {
    35 //i get matched with a free-matched one
    36             int nowt=2*t,nowj=j-1;
    37 //the biggest one become free-matched
    38             if (nowt&(1<<(k-1)))
    39             {
    40                 nowt-=1<<(k-1);
    41                 ++nowj;
    42             }
    43             res=(res+tryit(i-1,nowj,nowt))%modp;
    44         }
    45         int nowt=2*t+1,nowj=j;
    46         if (nowt&(1<<(k-1)))
    47         {
    48             nowt-=1<<(k-1);
    49             ++nowj;
    50         }
    51         res=(res+tryit(i-1,nowj,nowt))%modp;
    52         f[i][j][t]=res;
    53         return res;
    54     }
    55     public:
    56     int getNumber(int N, int K)
    57     {
    58         n=N,k=K;
    59         memset(vis,false,sizeof(vis));
    60         memset(f,0,sizeof(f));
    61         return tryit(2*n,0,0);
    62     }
    63 };

    Hard(1000pts):

    题目大意:现在有一个数列,它是1~n的一个排列,我们每次可以进行一次操作:选取一段l~r,将l~r的数都变成l~r的最大值,现在可以进行不超过K次操作,求最后一共有多少种不同的可能。数据满足n<=200,k<=200。

    怎么这题又是dp呀,自古TC出DP~

    在dp之前,我们需要对这个奇奇怪怪的操作挖掘一些性质。

    首先,一开始的n个数是不相同的,所以在整个操作过程中的任何时刻,相同的数永远都是连续的一段。

    其次,在某次操作前,i的一段在j的一段的前面,那么操作之后i的这一段依然在j的这一段的前面。

    有了这两个性质,差不多就可以dp了,我们考虑如何表述一个状态。

    k表示当前最多可以操作几步,i表示当前从小到大判定第几个数,j表示新的数列的前j个数已经被确定,

    那么我们就有f[k][i][j]=sigma(f[k-1][i-1][r])。

    然而这样是O(n^4)的,对于n<=200显然会爆炸,我们考虑进行进一步的优化,

    我们预处理出第i为什么情况下能变成j,那么一定是在原数列中,这两个数位置之前的数都小于等于j,

    这样我们可以先预处理出某个位置可以变成什么数,时间复杂度O(n^3),

    然后我们继续dp,除了上面描述的k,i,j,我们再用一个state表示当前是否处于匹配状态,显然state只能是true或者false,

    于是考虑f[k][i][j][state]:

    如果j>n,那么这已经是一个合法方案了,直接返回1;

    如果i>n,那么这个方案不合法,直接返回0;

    如果这两种情况都不满足,那么有两种情况:

    第一种是当前数i不参与匹配,对答案贡献度是f[k][i+1][j][false];

    第二种是当前数i参与匹配,我们没有必要再枚举它匹配到哪个位置,直接f[k-1][i][j+1][true]就可以了。

    这里dp就完成了,时间复杂度O(n^3)。

    所以整个题时间复杂度O(n^3),代码如下:

     1 #include <bits/stdc++.h>
     2 #define Maxn 207
     3 #define modp 1000000007
     4 using namespace std;
     5 int f[Maxn][Maxn][Maxn][2];
     6 bool vis[Maxn][Maxn][Maxn][2];
     7 bool check[Maxn][Maxn];
     8 int p[Maxn];
     9 int n,k;
    10 class AlienAndPermutation
    11 {
    12     int tryit(int k, int i, int j, int state)
    13     {
    14         if (vis[k][i][j][state]) return f[k][i][j][state];
    15         vis[k][i][j][state]=true;
    16         if (j>n)
    17         {
    18 //the situation is valid
    19             f[k][i][j][state]=1;
    20             return f[k][i][j][state];
    21         }
    22         if (i>n)
    23         {
    24 //the situation is invalid
    25             f[k][i][j][state]=0;
    26             return f[k][i][j][state];
    27         }
    28 //the ith number isn't used
    29         int res=tryit(k,i+1,j,0);
    30 //the ith number is used
    31         if (check[i][j])
    32         {
    33             if (state==1||(i==j)) res=(res+tryit(k,i,j+1,state))%modp;
    34             else if (k>0) res=(res+tryit(k-1,i,j+1,1))%modp;
    35         }
    36         f[k][i][j][state]=res;
    37         return res;
    38     }
    39     public:
    40     int getNumber(vector <int> P, int K)
    41     {
    42         n=P.size(),k=K;
    43         for (int i=1;i<=n;i++) p[i]=P[i-1];
    44         memset(check,true,sizeof(check));
    45         for (int i=1;i<=n;i++)
    46             for (int j=1;j<=n;j++)
    47                 for (int t=min(i,j);t<=i||t<=j;t++)
    48                     if (p[t]>p[i]) check[i][j]=false;
    49         memset(f,0,sizeof(f));
    50         memset(vis,false,sizeof(vis));
    51         return tryit(k,1,1,0);
    52     }
    53 };
  • 相关阅读:
    在Android studio中,测试输出数组中最大子数组的和
    我所理解的软件开发模式
    java实现随机输出300题四则运算
    Demo(3月28日)
    关于构建之法中小飞问题的个人看法
    对搭档代码的一些意见
    项目复审
    安卓UI测试(基于android studio环境 espresso框架)
    读构建之法后的一些个人感受
    思考题
  • 原文地址:https://www.cnblogs.com/Tommyr7/p/6868111.html
Copyright © 2011-2022 走看看