VIJOS基础动态规划试题泛做-I

一大波水题正在来袭

P1014 旅行商简化版

一道卡精度的坑爹题

做法很裸。我们将此题看作一道类似于方格取数的双进程的dp。容易考虑到,如果由(i,j)跳至(i,j+2),那么未跳的那个进程必须经过(j+1,x),否则不满足定义。我们通过这个性质定义一部分的方程。同时,通过这个方法,我们可以保证,1~min(i,j)这一段一定已经取得最小值,而对于一个最优化的路径,一定满足这个情况,也就是我们找到了一个最优子结构。

#include <stdio.h>
#include <algorithm>
#include <utility>
#include <math.h>
#include <iostream>
using namespace std;
#define x first
#define y second
double dis[1001][1001];
pair < double , double > p[1001];
double dp[1001][1001]; //表示每次到了哪两个点 
int N;
double EucDis( int a , int b ) 
  {
            return sqrt((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y));
 }
int main() 
 {
           scanf("%d",&N);
           for ( int i=1;i<=N;i++)
               scanf("%lf%lf",&p[i].x,&p[i].y);
           sort(p+1,p+1+N);
           for ( int i=1;i<=N;i++)
             for ( int j=i+1;j<=N;j++)
               dis[i][j]=EucDis(i,j);
           for ( int i=1;i<=N;i++)
             for ( int j=1;j<=N;j++)
               dp[i][j]=1e24;
           dp[2][1]=dis[1][2];
           for ( int i=3;i<=N;i++)
             for ( int j=1;j<i;j++)
               {
                   if(j<i-1)
                            dp[i][j]=dp[i-1][j]+dis[i-1][i];
                   else
                            {
                                                            for ( int k=1;k<j;k++)
                                                              dp[i][j]=min(dp[i][j],dp[j][k]+dis[k][i]);
                            }
               }
           printf("%.2lf\n",dp[N][N-1]+dis[N-1][N]);
           return 0;
}

P1117 数的划分

NOIP2001

组合计数的经典例题。我们定义状态为dp[i][j],其中第一维是当前的数字的数值,第二维是加了多少个符号。那么有两种可能,即:没有任何划分部分为一,此时的可能数等于dp[i-j][j],可以形象的看作为i-j划分为{a1-1,a2-1,a3-1,….,aj-1}的方案数等于i划分为{a1,a2,a3,….,aj}。如果有任意部分为一,那么无论它是否还有一,dp[i-1][j-1]为此时的可能性。对此求和即可。

#include <iostream>
#include <cstring>
using namespace std;
int N,K;
int dp[201][7]; 
int main()
 {
    memset(dp,0,sizeof(dp));
    cin >> N >> K;
    dp[0][0]=1;
    for ( int i=1;i<=N;i++)
       for ( int j=1;j<=K;j++)
          if(i>=j)
             dp[i][j]=dp[i-j][j]+dp[i-1][j-1];
    cout << dp[N][K] << endl;;
 }

P1057 盖房子

一道前缀和相关题,不知道为什么分类在DP下。

做法很简明。我们定义 dp[i][j] 为以 (i,j) 为矩形右下顶点的坐标。如果这点可以盖房子,那么我们只需要讨论:
1、 dp[i1][j1] 外推到 dp[i][j] ,即直接以原情况外推。
2、 dp[i][j1] dp[i1][j1] 二者较小的那个(因为瑕疵可能并不是包括在两个矩形共同覆盖的区域里的)。
那么方程易推得。这道题其实并不需要存地图,然而存了也无所谓。

#include <stdio.h>
#include <string.h>
#define min(a,b) ((a)<(b)?a:b)
#define max(a,b) ((a)<(b)?b:a)
int map[1001][1001],f[1001][1001];
int main()
 {
    int N,M;
    scanf("%d%d",&N,&M);
    for(int i=1;i<=N;i++)
     for(int j=1;j<=M;j++)
      scanf("%d",&map[i][j]);
    memset(f,0,sizeof(f));
    int Ans=-1;
    for(int i=1;i<=N;i++)
     for(int j=1;j<=M;j++)
      if(map[i][j])
       Ans=max(Ans,f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+1);
    printf("%d\n",Ans);
    return 0;   
 }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值