• 2020 HZNU Winter Training Day 4


    POJ 2279

    对于题目,我们将学生按高度从高到矮安排,那么这样的话其实在每一行的层面肯定是可以保证左边比右边高的,因为前面安排的都是更高的学生。
    安排新的学生时,对于每一行有如下考虑:
    1.该行未满
    2.行号为1,或者该行的学生数比上一行少(这样的话,就能保证如果新插入学生,那么这个学生的高度也比上一行同一位置的学生高度低)

    f[a1][a2][a3][a4][a5]表示每一行学生数
    边界条件:f[0][0][0][0][0]=1;

    坑点:MLE,定义dp数组的时候要根据输入的行号来定义,不然会MLE

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<list>
    #include<map>
    #include<set>
    #include<sstream>
    #include<string>
    #include<vector>
    #include<cstdio>
    #include<ctime>
    #include<bitset>
    #include<algorithm>
    #include<string.h>
    using namespace std;
    typedef long long ll;
    #define lson l , mid , rt << 1
    #define rson mid + 1 , r , rt << 1 | 1
    
    int read() {
        int ans = 0;
        int flag = 1;
        char ch = getchar();
        while ((ch > '9' || ch < '0') && ch != '-') ch = getchar();
        if (ch == '-') flag = -1, ch = getchar();
        while (ch >= '0' && ch <= '9') ans = ans * 10 + ch - '0', ch = getchar();
        return ans * flag;
    }
    
    int n[6];
    int main() {
        int k;
        while (~scanf("%d",&k)) {
            if (k == 0) break;
            for (int i = 0; i < 6; i++){
                n[i] = 0;
            }
            for (int i = 1; i <= k; i++){
                cin >> n[i];
            } 
            ll dp[n[1] + 1][n[2] + 1][n[3] + 1][n[4] + 1][n[5] + 1];
            memset(dp, 0, sizeof dp);
            dp[0][0][0][0][0] = 1;//每维均为0时安排种类数为1。
            for (int a1 = 0; a1 <= n[1]; a1++) {
                for (int a2 = 0; a2 <= n[2]; a2++) {
                    for (int a3 = 0; a3 <= n[3]; a3++) {
                        for (int a4 = 0; a4 <= n[4]; a4++) {
                            for (int a5 = 0; a5 <= n[5]; a5++) {
                                if (a1 < n[1]) dp[a1 + 1][a2][a3][a4][a5] += dp[a1][a2][a3][a4][a5];//如果第一维小于n[1],就还可以往第一排安排人
                                if (a1 > a2 && a2 < n[2]) dp[a1][a2 + 1][a3][a4][a5] += dp[a1][a2][a3][a4][a5];//如果第一维人数大于第二维人数并且第二维人数小于n[2],就可以在第二排安排人
                                if (a1 > a3 && a2 > a3 && a3 < n[3]) dp[a1][a2][a3 + 1][a4][a5] += dp[a1][a2][a3][a4][a5];
                                if (a1 > a4 && a2 > a4 && a3 > a4 && a4 < n[4]) dp[a1][a2][a3][a4 + 1][a5] += dp[a1][a2][a3][a4][a5];
                                if (a1 > a5 && a2 > a5 && a3 > a5 && a4 > a5 && a5 < n[5]) dp[a1][a2][a3][a4][a5 + 1] += dp[a1][a2][a3][a4][a5];
                            }
                        }
                    }
                }
            }
            cout << dp[n[1]][n[2]][n[3]][n[4]][n[5]] << endl;
        }
        return 0;
    }
    View Code

    POJ 2096 概率DP writed by kuangbin
    题意:一个软件有s个子系统,会产生n种bug。某人一天会发现一个bug,这个bug属于一个子系统,属于一个分类,每个bug属于某个子系统的概率是1/s,属于某种分类的概率是1/n。问发现n种bug,每个子系统都发现bug的天数的期望。
    dp[i][j]表示已经发现i种bug,j个系统的bug,达到目标还需要的天数的期望
    dp[n][s]=0,求解dp[0][0]的值
    dp[i][j]的状态转化:
    dp[i][j],发现一个bug属于已有的i个分类和j个系统。概率为(i/n)*(j/s)
    dp[i][j+1],发现一个bug属于已有的i个分类,不属于已有的j个系统。概率为(i/n)*(1-j/s)
    dp[i+1][j],发现一个bug不属于已有的i个分类,属于已有的j个系统。概率为(1-i/n)*(j/s)
    dp[i+1][j+1],发现一个bug不属于已有的i个分类和已有的j个系统。概率为(1-i/n)*(1-j/s);
    方程可以整理为:dp[i][j]=(i/n)*(j/s)*dp[i][j]+(i/n)*(1-j/s)*dp[i][j+1]+(1-i/n)*(j/s)*dp[i+1][j]+(1-i/n)*(1-j/s)*dp[i+1][j+1]+1
    解得: dp[i][j]=(i*(s-j)*dp[i][j+1]+(n-i)*j*dp[i+1][j]+(n-i)*(s-j)*dp[i+1][j+1]+n*s)/(n*s-i*j);

    #include<cstdio>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<string>
    #include<set>
    #include<vector>
    #include<map>
    #include<queue>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pr;
    const int maxn=1e6+9;
    const int N=1<<20;
    const int inf=0x3f3f3f3f;
    const ll INF=0x3f3f3f3f3f3f3f3f;
    const ll mod=1e9+7;
    const int temp=233;
    const double eps=0.0000001;
    const double PI=acos(-1);
    const int dx[] = {0,0,1,1,1,-1,-1,-1};
    const int dy[] = {-1,1,1,-1,0,1,-1,0};
    inline int read(){
        int num=0, w=0;char ch=0;
        while (!isdigit(ch)) {w|=ch=='-';ch = getchar();}
        while (isdigit(ch)) {num = (num<<3) + (num<<1) + (ch^48);ch = getchar();}return w? -num: num;
    }
    int n,s;
    double  dp[1010][1010];
    int main(){
        while(~scanf("%d%d",&n,&s)){
            dp[n][s]=0;
            for(int i=n;i>=0;i--)
                  for(int j=s;j>=0;j--)
                  {
                      if(i==n&&j==s)continue;
                      dp[i][j]=(i*(s-j)*dp[i][j+1]+(n-i)*j*dp[i+1][j]+(n-i)*(s-j)*dp[i+1][j+1]+n*s)/(n*s-i*j);
                  }
                printf("%.4f
    ",dp[0][0]);
        }
        return 0;
    }
    View Code

    C题:

    dp[ i ][ 0 ] 就代表, 你的 第一个人取 1 ~ i 的花费。

    dp[ i ][ 1 ] 就表示, 第一个人 拿了 1 ~ i 的前缀 1 ~ pos 部分, 第二个人拿了, 剩下的 pos ~ i 部分的最少花费。

    d[i][2]表示第二个人拿完到pos最后一人拿最后剩下的到i。所要用的最小花费。

    状态转移方程为:

            dp[i][0] = dp[i - 1][0] + (A[i] != 0 ? 1 : 0);

            dp[i][1] = min(dp[i - 1][0], dp[i - 1][1]) + (A[i] != 1 ? 1 : 0);

            dp[i][2] = min(dp[i - 1][0], min(dp[i - 1][1], dp[i - 1][2])) + (A[i] != 2 ? 1 : 0);

    #include<stdio.h>
    #include<algorithm>
    #define LL long long
    using namespace std;
    
    const int MAXN = 2e5 + 15;
    int f[MAXN];
    int dp[MAXN][3];
    int main() {
    
        int a, b, c; scanf("%d %d %d", &a, &b, &c);
        int n = a + b + c;
        for(int i = 1; i <= a; i++) {
            int x; scanf("%d", &x); 
            f[x] = 0;
        }
        for(int i = 1; i <= b; i++) {
            int x; scanf("%d", &x); 
            f[x] = 1;
        }
        for(int i = 1; i <= c; i++) {
            int x; scanf("%d", &x); 
            f[x] = 2;
        }
        for(int i = 1; i <= n; i++) {
            dp[i][0] = dp[i - 1][0] + (f[i] != 0 ? 1 : 0);
            dp[i][1] = min(dp[i - 1][0], dp[i - 1][1]) + (f[i] != 1 ? 1 : 0);
            dp[i][2] = min(dp[i - 1][0], min(dp[i - 1][1], dp[i - 1][2])) + (f[i] != 2 ? 1 : 0);
        }
    
    
        printf("%d
    ", min(dp[n][0], min(dp[n][1], dp[n][2])));
    }
    View Code

    D题:

    题解:创建两个dp数组,dp_z[i]dp_f[i],表示和元素a[i]之前和a[i]连接的的所有正区间和负区间个数。

    转移方程为:

    if (a[i] > 0){

    dp_z[i] = dp_z[i - 1] * 2 - dp_z[i - 2] + 1;

    dp_f[i] = dp_f[i - 1] * 2 - dp_f[i - 2];

    }

    else if (a[i] < 0){

    dp_z[i] = dp_f[i - 1] + dp_z[i - 1] - dp_f[i - 2];

    dp_f[i] = dp_f[i - 1] + dp_z[i - 1] - dp_z[i - 2] + 1;

    }

    else {

    dp_z[i] = dp_z[i - 1];

    dp_f[i] = dp_f[i - 1];

    }

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 2e5 + 15;
    
    int n;
    
    int main(){
        ll a[MAXN], dp_z[MAXN]={0}, dp_f[MAXN]={0};
        scanf("%d", &n);
        for (int i = 2; i <= n + 1; i++)
            scanf("%lld", &a[i]);
        for (int i = 2; i <= n + 1; i++){
            if (a[i] > 0){
                dp_z[i] = dp_z[i - 1] * 2 - dp_z[i - 2] + 1;
                dp_f[i] = dp_f[i - 1] * 2 - dp_f[i - 2];
            }
            else if (a[i] < 0){
                dp_z[i] = dp_f[i - 1] + dp_z[i - 1] - dp_f[i - 2];
                dp_f[i] = dp_f[i - 1] + dp_z[i - 1] - dp_z[i - 2] + 1;
            }
            else {
                dp_z[i] = dp_z[i - 1];
                dp_f[i] = dp_f[i - 1];
            }
        }
        printf("%lld %lld
    ", dp_f[n + 1], dp_z[n + 1]);
        return 0;
    }
    View Code

    codeforces629C Famil Door and Brackets
    题意:给你一个长度为m的括号匹配串s(不一定恰好匹配),让你在这个串的前面和后面加上一些括号匹配串,使得这个括号串平衡,即p+s+q达到平衡(平衡的含义是对于任意位置的括号前缀和大于等于0,且最后的前缀和为0)(前缀和为'('的数量-')'的数量)。最后串的长度为n。
    解法:枚举这个字符窜前面p字符窜的长度,因为总长度为n,所以后面q字符窜的长度就为n-m-p。p字符窜要满足任意位置的前缀和大于等于0,所以保证p的前缀和+s中最小的前缀和minn>=0,p的方案确定了,q的方案也就确定了。我们使用dp[i][j]表示长度为i的字符窜,平衡度为j的方案数,可以先预处理出来dp的值。然后枚举p的长度和平衡度,ans+=dp[p][c]*dp[n-m-p][now+c](now为m串的平衡度),根据()对称性,长度为i,平衡度为-j的dp值也为dp[i][j]

    #include<cstdio>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<string>
    #include<set>
    #include<vector>
    #include<map>
    #include<queue>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pr;
    const int maxn=1e6+9;
    const int N=1<<20;
    const int inf=0x3f3f3f3f;
    const ll INF=0x3f3f3f3f3f3f3f3f;
    const ll mod=1e9+7;
    const int temp=233;
    const double eps=0.0000001;
    const double PI=acos(-1);
    const int dx[] = {0,0,1,1,1,-1,-1,-1};
    const int dy[] = {-1,1,1,-1,0,1,-1,0};
    inline int read(){
        int num=0, w=0;char ch=0;
        while (!isdigit(ch)) {w|=ch=='-';ch = getchar();}
        while (isdigit(ch)) {num = (num<<3) + (num<<1) + (ch^48);ch = getchar();}return w? -num: num;
    }
    int n,m;
    ll ans;
    char s[maxn];
    ll dp[3000][3000];
    int main(){
        scanf("%d%d",&n,&m);
        if(n&1)
        {
            printf("0
    ");
            return 0;
        }
        scanf("%s",s);
        dp[0][0]=1;
        for(int i=1;i<=n-m;i++){//预处理前缀为i的,(-)值为j的方案数目 
            for(int j=0;j<=i;j++){
                if(j>0) dp[i][j]=(dp[i-1][j+1]+dp[i-1][j-1])%mod;
                else dp[i][j]=dp[i-1][j+1];
            }
        }
        int minn=inf;//记录下s中(-)值的最小值 
        int num=0;
        for(int i=0;i<m;i++){
            if(s[i]=='(') num++;
            else num--;
            minn=min(minn,num);
        }
        int len=n-m;
        ans=0;
        for(int i=0;i<=len;i++){
            for(int j=0;j<=i;j++){
                if(j+minn<0||j+num>len-i) continue;//前缀p+s括号无法平衡||p+s+q无法平衡 
                ans=(ans+dp[i][j]*dp[len-i][j+num]%mod)%mod;
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    Codeforces580D Kefa and Dishes
    题意:菜单上有n道菜,你可以点m样,但是不同点同样的菜。每样菜有它自己的幸福感,然后还加入了k个规则,比如在吃了第i样菜之后,再吃第j样菜,可以获得c的幸福感,问最大的幸福感。
    解法:n最大为18,数据一看显然是状压dp,菜的选择状态用二进制来存储。dp[i][j]表示i状态下最后一到菜吃的是第j道菜取得的最大幸福感。若二进制状态下的s的第i位等于1,而第j位等于0,dp[s∣(1<<j)][j]=max(dp[s∣(1<<j)][j],dp[s][i]+a[j]+mp[i][j]); 特殊规则使用邻接矩阵存储。二进制1表示吃,0表示不吃,遍历1<<n种状态,二进制状态下1的个数和=m,则状态合法,更新最大值

    #include<cstdio>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<string>
    #include<set>
    #include<vector>
    #include<map>
    #include<queue>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pr;
    const int maxn=1e6+9;
    const int N=1<<18;
    const int inf=0x3f3f3f3f;
    const ll INF=0x3f3f3f3f3f3f3f3f;
    const ll mod=1e9+7;
    const int temp=233;
    const double eps=0.0000001;
    const double PI=acos(-1);
    const int dx[] = {0,0,1,1,1,-1,-1,-1};
    const int dy[] = {-1,1,1,-1,0,1,-1,0};
    inline int read(){
        int num=0, w=0;char ch=0;
        while (!isdigit(ch)) {w|=ch=='-';ch = getchar();}
        while (isdigit(ch)) {num = (num<<3) + (num<<1) + (ch^48);ch = getchar();}return w? -num: num;
    }
    int n,m,k;
    int a[maxn];
    int mp[100][100];
    ll dp[N][20],ans;//dp[i][j]表示状态为i下,最后一个吃的是j的值 
    int main(){
        scanf("%d%d%d",&n,&m,&k);
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
        }
        int x,y,c;
        for(int i=0;i<k;i++){
            scanf("%d%d%d",&x,&y,&c);
            mp[x-1][y-1]=c;
        }
        for(int i=0;i<n;i++){
            dp[1<<i][i]=a[i];   //状态压缩 
        }
        int S=1<<n;
        ans=0;
        for(int s=0;s<S;s++){
            int num=0;
            for(int i=0;i<n;i++){
                if(s&(1<<i)){
                    num++;//当前状态包括第i个菜时num++ 
                    for(int j=0;j<n;j++){//当前状态下最后一道菜吃i时吃第j个菜 
                        if(s&(1<<j)) continue;//j已经被吃则跳过 
                        int new_s=s|(1<<j);//更新吃j后的状态 
                        dp[new_s][j]=max(dp[new_s][j],dp[s][i]+a[j]+mp[i][j]);
                    }
                }
            }
            if(num==m){//吃了m个菜时,更新当前状态下最大值 
                for(int i=0;i<n;i++){
                    if(s&(1<<i)) ans=max(ans,dp[s][i]);//当前状态下是否吃了i 
                }
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    G题:

    题解:考虑dp的做法,dp[i][j]代表以第i个数为右端点,长度求余m的值为j时的最大值。

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

    dp[i][j]=max(dp[i-1][m-1]+a[i]-k,a[i]-k)(j==1)

    dp[i][j]=dp[i-1][m-1] + a[i];

    注意:j可表示长度,但会超时,因此想到取模。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    const int N = 3e5 + 5;
    ll a[N];
    ll dp[N][20];
    int main()
    {
        int n, m, k;
        cin >> n >> m >> k;
        a[0] = 0;
        for (int i = 1; i <= m; i ++)dp[0][i] = -1e10;
        ll ans = 0;
        for (int i = 1; i <= n; i ++){
            scanf("%lld", &a[i]);
            for (int j = 1; j <= m; j ++){
                if (j == 1)dp[i][j] = max(a[i] - k, dp[i - 1][m] + a[i] - k);
                else dp[i][j] = dp[i - 1][j - 1] + a[i];
                ans = max(ans, dp[i][j]);
            }
        }
        printf("%lld
    ", ans);
     
        return 0;
    }
    View Code

    约瑟夫环变形问题
    首先先明确约瑟夫问题,已知n个人围成一圈(编号:1,2,3,…,n),从编号为1的人开始报数,报数为m的那个人出列;从他的下一个人又从1开始数,同样报数为m的人出列;依此循环下去,直到剩余一个人。求最后这一个人在最开始的序列中编号是几号?

    以上我们将问题转换为模式相同且规模逐渐缩小的问题,当规模最小即只有一个人n=1时,报数为m-1的人出列,最后出列的人编号为0;当n=2时,报数为m-1的人出列,最后出列人的编号是多少?应该是只有一个人时得到最后出列的序号加上m(因为报数为m-1的人已经出列,剩下那个人才最后出列所以才加上m)
    n=1时,f(1)=0;
    n=2时,f(2)=[f(1)+m]%2;
    n=3时,f(3)=[f(2)+m]%3;
    验证结果:2个人围成一圈,数到3的那人出列,求最后那个人的编号?n=2,m=3
    f(2)=[f(1)+m]%2=[0+3]%2=1
    最后结果加1,则result=2;

    正常约瑟夫环是从1号开始,如果从m号,那么就是报数延迟到了m,那最后报数的人也会相应延迟m。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<list>
    #include<map>
    #include<set>
    #include<sstream>
    #include<string>
    #include<vector>
    #include<cstdio>
    #include<ctime>
    #include<bitset>
    #include<algorithm>
    #include<string.h>
    using namespace std;
    typedef long long ll;
    #define lson l , mid , rt << 1
    #define rson mid + 1 , r , rt << 1 | 1
    
    int read() {
        int ans = 0;
        int flag = 1;
        char ch = getchar();
        while ((ch > '9' || ch < '0') && ch != '-') ch = getchar();
        if (ch == '-') flag = -1, ch = getchar();
        while (ch >= '0' && ch <= '9') ans = ans * 10 + ch - '0', ch = getchar();
        return ans * flag;
    }
    
    const int maxn = 10010;
    int dp[maxn];
    int main()
    {
        int n, k, m;
        while (scanf("%d%d%d", &n, &k, &m) != EOF) {
            if (n == 0 && k == 0 && m == 0) break;
            dp[1] = 0;
            for (int i = 2; i < n; i++) {
                dp[i] = (dp[i - 1] + k) % i;
            }
            printf("%d
    ", (dp[n - 1] + m) % n + 1);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    02.零成本实现WEB性能测试-基于APACHE JMETER
    Apache JMeter--网站自动测试与性能测评
    01.天幕红尘
    php-fpm介绍及配置
    Linux环境下搭建php开发环境的操作步骤
    Linux下PHP开发环境搭建
    nginx 502 Bad Gateway 错误问题收集
    Nginx配置文件详细说明
    Linux下查看MySQL的安装路径
    LINUX下YUM源配置
  • 原文地址:https://www.cnblogs.com/fengzhongzhuifeng/p/12201642.html
Copyright © 2020-2023  润新知