sduacm16级寒假训练 动态规划(二)

传送门

A

/*
题目大意:一个人去买C瓶可乐,每瓶八块,有1 5 10面值的硬币若干,求出最少投币数。售货机会尽可能的找更少的硬币个数(也就是投一个十块三个一块,会找一个五块= =)
解题思路:dp[i][j][k],ijk记录三个硬币状态,dp记录使用个数,递归加优化
*/
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
int cokes,one,five,ten;
int dp[700][200][100];
int vis[700][200][100];

int DP(int n,int a,int b,int c)
{
    if(vis[a][b][c]==1)    //访问过的直接return
        return dp[a][b][c];
    else if(n==0) //全部买完
    {
        vis[a][b][c]=1;
        dp[a][b][c]=0;
        return dp[a][b][c];
    }
    else
    {
        dp[a][b][c] = 1000000;
        if(a>=8)
            dp[a][b][c] = min(dp[a][b][c],DP(n-1,a-8,b,c)+8);
        if(a>=3&&b>=1)
            dp[a][b][c] = min(dp[a][b][c],DP(n-1,a-3,b-1,c)+4);
        if(b>=2)
            dp[a][b][c] = min(dp[a][b][c],DP(n-1,a+2,b-2,c)+2);
        if(c>=1)
            dp[a][b][c] = min(dp[a][b][c],DP(n-1,a+2,b,c-1)+1);
        if(c>=1&&a>=3)
            dp[a][b][c] = min(dp[a][b][c],DP(n-1,a-3,b+1,c-1)+4);
        vis[a][b][c] = 1;
        return dp[a][b][c];
    }
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>cokes>>one>>five>>ten;
        memset(vis,0,sizeof(vis));
        cout<<DP(cokes,one,five,ten)<<endl;

    }
    return 0;
}

/*
题目大意:L先生喜欢用三根筷子(A<B<C),生日那天,他邀请其K人一起用,还有8个家人,共K+8人,要挑出K+8套筷子,保证有一个是最长的,
同时较短的两根的 badness:(A-B)^2最小,。T组数据,每组输入K(客人),N(筷子个数)以及N个筷子的长度(非递减顺序),输出所有badness的合的最小值。
解题思路:先把筷子长度倒序,保证每一套都会有一根最长的,然后遍历即可。 状态转移方程dp[i][j]=min(dp[i][j-1],dp[i-1][j-2]+chops[j]-chops[j-1]^2)
第i个人,第j根筷子
*/
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
int T,K,N;
int chops[10000];
int dp[10000][10000];

int cmp(int a,int b)
{
    return a>b;  //a>b是逆序
}

int main()
{
    cin>>T;
    while(T--)
    {
        cin>>K>>N;
        for(int i = 1;i<=N;i++)
            scanf("%d",&chops[i]);
        sort(chops+1,chops+N+1,cmp);
        memset(dp,0,sizeof(dp));
        for(int i = 1;i<=K+8;i++)
        {
            int t = i*3;
            dp[i][t] = dp[i-1][t-2]+(chops[t-1]-chops[t])*(chops[t-1]-chops[t]);   //先挑出一套筷子
            for(int j = t+1;j<=N;j++)
            {
                dp[i][j] = min(dp[i][j-1],dp[i-1][j-2]+(chops[j-1]-chops[j])*(chops[j-1]-chops[j]));  //选第j根和不选第j根
            }
        }

    cout<<dp[K+8][N]<<endl;

    }
    return 0;
}


/*
题目大意:一个小姐姐被关在有R*C个小房间的迷宫里了,她要从左上角(1,1)逃到(R,C),每次移动可能向下,右或者留在原地,R行数据,每行三个浮点数代表原地,→,↓的概率
每次移动需要消耗两点法力,求需要多少法力才能出去
解题思路:概率DP,dp[i][j] = p[i][j][1]*dp[i][j] + p[i][j][2]*dp[i][j+1] + p[i][j][3]*dp[i+1][j] + 2(其中p[i][j][k]代表在点(i,j)选择第k种走法的概率),
再化简一下:dp[i][j] = (p[i][j][2]*dp[i][j+1] + p[i][j][3]*dp[i+1][j] + 2)/(1-p[i][j][1])
  因为dp[r][c]=0,即当在点(r,c)时,他不需要花费魔法值就可以到达(r,c),这样就可以从后往前递推了
*/
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
int R,C;
double map[1010][1010][3];
double dp[1010][1010];
int main()
{
    while(~scanf("%d%d",&R,&C))
    {
    for(int i = 1;i<=R;i++)
        for(int j = 1;j<=C;j++)
            for(int k = 0;k<3;k++)
                scanf("%lf",&map[i][j][k]);
    memset(dp,0,sizeof(dp));
    for(int i = R;i>=1;i--)
        for(int j = C;j>=1;j--)
        {
            if(i==R&&j==C)
                continue;
            if(fabs(map[i][j][0]-1)<1e-7)
                continue;
            dp[i][j] = (dp[i][j+1]*map[i][j][1]+dp[i+1][j]*map[i][j][2]+2)/(1-map[i][j][0]);
        }

    printf("%.3lf\n",dp[1][1]);
    }
    return 0;
}

扫描二维码关注公众号,回复: 1726101 查看本文章

 D

 

/*
题目大意:V个村庄(在x轴上)中建立P个邮局,求每个村庄到最近邮局的最短距离和
解题思路:区间DP,首先先求出在连续几个村庄建立一个邮局的最短距离,dis[i][j]表示村庄i到j建立一个邮局的距离则 dp[i][j]=dp[i][j-1]+x[j]-x[(i+j)/2]  x[i]记录村庄坐标
所以对于dp 有在前k个村庄建立j-1个邮局,在k+1到i建立下一个邮局。用dp[i][j]表示前i个村庄建立j个邮局
即dp[i][j] = min(dp[i][j],dp[k][j-1]+dis[k+1][i])
*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
int v,p;
int dp[310][50];
int dis[310][310];
int x[310];
int main()
{
    cin>>v>>p;
    for(int i = 1;i<=v;i++)
        cin>>x[i];
    memset(dp,0,sizeof(dp));
    for(int i = 1;i<=v;i++)
        for(int j = i+1;j<=v;j++)
        {
            dis[i][j] = dis[i][j-1]+x[j]-x[(j+i)/2];  //i 到 j 村庄之间建立一个 postoffice 的 minimum distance
        }
    for(int i = 1;i<=v;i++)
    {
        dp[i][i] = 0; //自己村的邮局
        dp[i][1] = dis[1][i];  //前i个村建立一个邮局
    }
    for(int j = 2;j<=p;j++)
        for(int i = j+1;i<=v;i++)
        {
            dp[i][j] = 100000000;
            for(int k = j-1;k<i;k++)
            {
                dp[i][j] = min(dp[i][j],dp[k][j-1]+dis[k+1][i]);
            }
        }
    cout<<dp[v][p]<<endl;
    return 0;
}

 E

 

 /*
题目大意:给一组括号,按照规则配对输出,尽可能少添加符号
解题思路:左右对应时,dp[i][j] = dp[i][j],用pos=-1记录 ,不对应时拆成两部分 dp[i][j] = min(dp[i][j],dp[i][mid]+dp[mid][j]) 用pos记录拆分点
*/
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
int dp[101][101],pos[101][101];
char str[101];
using namespace std;

void print(int i,int j)
{
    if(i>j)
        return;
    if(i==j)  //仅有一个bracket
    {
        if(str[i]=='('||str[i]==')')
            printf("()");
        else
            printf("[]");
    }
    else if(pos[i][j]==-1)  //左右对应
    {
        printf("%c",str[i]);
        print(i+1,j-1);
        printf("%c",str[j]);
    }
    else                   //拆分成两部分
    {
        print(i,pos[i][j]);
        print(pos[i][j]+1,j);
    }
}

int main()
{
    scanf("%s",&str);
    int len = strlen(str);
    memset(dp,0,sizeof(dp));
    for(int i = 0;i<len;i++)
        dp[i][i] = 1;

    for(int k = 1;k<len;k++)
    {
        for(int i = 0;i+k<len;i++)
        {
            int j = i+k;
            dp[i][j] = 1000000;
            if((str[i]=='('&&str[j]==')')||(str[i]=='['&&str[j]==']'))
            {
                dp[i][j] = dp[i+1][j-1];
                pos[i][j] = -1;
            }
            for(int mid = i;mid<j;mid++)
            {
                if(dp[i][j]>(dp[i][mid]+dp[mid+1][j]))
                {
                    dp[i][j] = dp[i][mid]+dp[mid+1][j];
                    pos[i][j] = mid;
                }
            }
        }
    }

    print(0,len-1);
    cout<<endl;            //记得换行!!不然会wa....
    return 0;
}


猜你喜欢

转载自blog.csdn.net/jemary_/article/details/54860853