动态规划测试test20170525

来源:互联网 发布:php抽象类和接口的作用 编辑:程序博客网 时间:2024/05/19 11:48

前言

由于自己的一些年级组那边的事情影响了当时考试时的心情,然后就A了第一题就没什么心思去搞其他题目了,这是很不好的。

题目

火 车 票(ticket.cpp)

Timelimit :0.1s Name :Ticket

一个铁路线上有 n(2<=n<=10000)个火车站,每个火车站到该线路的首发火车站距离都是已知的。任意两站之间的票价如下表所示:

站之间的距离 – X 票价 0XL1 C1 L1XL2 C2 L2XL3 C3

其中 L1L2L3C1C2C3 都是已知的正整数,且(1L1<L2<L3109, 1C1C2C3109)。显然若两站之间的距离大于 L3,那么从一站到另一站至少要买两张
票。注意:每一张票在使用时只能从一站开始到另一站结束。
任务
对于给定的线路,求出从该线路上的站 A 到站 B 的最少票价。
输入
输入文件的第一行为 6 个整数, L1,L2,L3,C1,C2,C3(1L1L2L3109,1C1C2C3109) ,这些整数由空格隔开.第二行为火车站的数量 N (2N10000).
第三行为两个不同的整数 A、B,由空格隔开。接下来的 N-1 行包含从第一站到其他站之间
的距离.这些距离按照增长的顺序被设置为不同的正整数。相邻两站之间的距离不超过 L3.
两个给定火车站之间行程花费的最小值不超过 109,而且任意两站之间距离不超过 109
输出
输出文件中只有一个数字,表示从 A 到 B 要花费的最小值.
样例输入
3 6 8 20 30 40
7
2 6
3
7
8
13
15
23
样例输出
70

题解:
这题目原来一本通的习题上有,不过不要优化O(n2) 可以过,这里要单调优化即可。
代码:

#include <cstdio>#include <cstring>#include <algorithm>#define Max(a,b) ((a)>(b)?(a):(b))#define Min(a,b) ((a)<(b)?(a):(b))typedef long long LL;const int maxn = 10000+10;const int INF = 2e9;inline int read(int &in) {    in=0;int f=1;char ch=getchar();    for(;ch<'0'||ch>'9';ch=getchar())        if(ch=='-') f=-1;    for(;ch>='0'&&ch<='9';ch=getchar())        in=in*10+ch-'0';    return in*f;}int n,sta,lst;int l[5],c[5];int k[5],p[5];int f[maxn];int dist[maxn],dis;namespace my_self {    void init() {        for(int i=1;i<maxn;i++)            f[i]=0;        read(l[1]);read(l[2]);read(l[3]);read(c[1]);read(c[2]);read(c[3]);        read(n);read(sta);read(lst);        dist[1]=0;        for(int i=2;i<=n;i++)            read(dist[i]);    }    void work() {        p[1]=l[1];p[2]=l[2];p[3]=l[3];k[1]=k[2]=k[3]=sta;        for(int i=sta+1;i<=lst;i++) {            dis=dist[i]-dist[i-1];f[i]=INF;            for(int j=1;j<=3;j++) {                if(dis<=p[j]) {                    p[j]-=dis;                    if(f[k[j]]+c[j]<f[i]) f[i]=f[k[j]]+c[j];                }                else {                    while(k[j]<i) {                        k[j]++;if(dist[i]-dist[k[j]]<=l[j]) break;                    }                    if((k[j]<=i) && (dist[i]-dist[k[j]]<=l[j])) {                        p[j]=l[j]-(dist[i]-dist[k[j]]);                        f[i]=Min(f[i],c[j]+f[k[j]]);                    }                    else p[j]=l[j];                }            }        }        printf("%d\n",f[lst]);    }}int main() {    freopen("ticket.in","r",stdin);    freopen("ticket.out","w",stdout);    my_self::init();    my_self::work();    return 0;}

艺术馆的火灾(fire.cpp)

背景描述
这幢古老的建筑是一个艺术馆,它珍藏着上百件绘画、雕塑以及其他艺术品,就连建筑本身也是一件艺术。但是,岁月并不在乎它的精致与美丽,时光在渐渐剥夺着这幢木屋的生命。终于,在一个月色昏暗的夜晚,它着火了。艺术馆是一幢两层的小楼,每一层有 N 个房间,每个房间中收藏的艺术品的价值都可以用一个正整数来表示。下面是一个 N=4 的例子。
  屋顶
40 50 30 60
30 30 40 20
在这个例子中,二层楼的第四个房间中艺术品的价值最大,为 60。而一层楼的第四个房间中艺术品的价值仅为 20。在消防队员火速赶到的时候,火势已经蔓延了整个建筑,消防队员们观察每个房间中的火势,将它们分别用一个正整数来表示。在上面的例子中,各房间中的火势可能如下。
  屋顶
50 40 50 70
40 50 20 30
你可以看到,二层楼的第四个房间中火势最强,为 70。而一层楼的第三个房间中火势较弱,为 20。
由于火情紧急,消防队员们准备使用一种新型的灭火器。这种灭火器只能发射 K 次,每次发射将覆盖一个矩形的区域(矩形的高度可以是 1 也可以是 2)。它的威力巨大,所到之处不但火焰会被扑灭,就连同在一处的艺术品也难以幸免。因此,如何善用这种灭火器成了最大的问题。一个例子:如果灭火器的一次发射覆盖了下图阴影所示的 2×2 矩形区域,那么这四个房间的火势和艺术品价值都将成为 0。
任务说明
给出艺术馆每层的房间数 N 和灭火器的发射次数 K,以及每个房间中艺术品的价值和火势,你需要决定灭火器每次应该怎样发射(也可以不发射),才能将这次火灾的损失降到最低限度。这个损失用你所摧毁的艺术品的总价值,加上剩余的火势总值(这些火焰将需要消防队员们亲身去扑灭)来衡量。
输入数据
输入文件的第一行有两个整数 N(1 <= N <= 100)、K(1<= K <= 10),分别表示艺术馆中每层的房间数和灭火器的发射次数。
接下来的两行每行有 N 个整数,其中第 4-i 行的第 j 个整数 Vi,j表示的是第 i层第 j 个房间中艺术品的价值(1 <= i <= 2,1 <= j <= N,1 <= Vi,j <= 10000)。
再接下来的两行每行也有 N 个整数,其中第 6-i 行的第 j 个整数 Fi,j 表示的是第 i 层第 j 个房间中的火势(1 <= i <=2,1 <= j <= N,1 <= Fi,j <= 10000)。
输出数据
你的输出文件应该有 K 行,每行有四个整数 L1、R1、L2、R2,表示你的灭火器的一次行动。如果灭火器这次不发射,那么这四个整数都为 0;否则这次发射所覆盖的矩形区域的左下角是第 L1 层的第 R1 个房间,右上角是第 L2 层的第R2 个房间。
注意:你的每次发射所覆盖的矩形区域必须位于小楼之内,并且矩形的面积至少为 0。即 1 <= L1 ,L2 <= 2,1 <= R1 , R2 <= N。
输入输出样例
fire.in
4 2
40 50 30 60
30 30 40 20
50 40 50 70
40 50 20 30
fire.out
1 1 1 2
2 3 2 4
样例解释
摧毁艺术品:30+60+30+30=150
剩余火势:50+40+20+30=140
最小损失:150+140=290
评分标准
每个测试点都有一定的时间限制。你的程序只有在规定的时限内输出解,并且按照你的方案来发射灭火器可以将损失降到最小,你才会得到相应测试点的分值;否则这个测试点不得分。
注意:本题的解不一定是唯一的。即对于一个测试数据,可能有多种发射灭火器的方案
都可以使损失达到最小。在这种情况下,你只要输出最优方案中的任意一种即可。

题解:
首先将问题转化成模型:在一个2×N的矩阵中,找出K个子矩阵。我们将这些子矩阵所包含的元素集合用S来表示的话,那么问题的目标就是使eSVe+eSFe这个值最小。
因为 eSFe+eSFe=Sum(F)(Sum(F)表示F矩阵中的元素总和),所以上面的式子可以化为 eSVe+(Sum(F)eSFe),而Sum(F)又是个常数,所以实际上问题的目标就是使eS(VeFe)最小。
下面是规划方程:
f(s,i,j)=minf(s,i1,j)f(s,i,j1)f(s1,t1,j)+sum(1,t,1,i)(1ti)f(s1,i,t1)+sum(2,t,2,j)(1tj)f(s1,t1,t1)+sum(1,t,2,min(i,j))(1tmin(i,j))
边界条件
f(0,i,j)=0(0i,jN) f(s,0,0)=0(0sk)

然后输出就得在状态转移时记录即可。

代码:

    #include <iostream>    #include <fstream>    #include <cassert>    using namespace std;    ifstream fin("Input.txt");    ofstream fout("Output.txt");    int room_n,shoot_k,v[3][101],f[3][101];    /*dp部分*/    int dp[101][5][11];//[i][j][k]:前i列已经最少有k个完整矩形、第i列的状态为j之后还能获得的最小损失    bool checked[101][5][11];    int memo(int i,int j, int k);    int loss(int i, int j); //memo调用的子过程,返回第i行决策为j时的净损失    /*最优解的构造部分*/    int build[101][5][11];  //记录(i,j,k)状态下要得到最优解,i+1列的状态    bool room[3][101];    void paint(int i, int j, int k);//在room中标出状态(i,j,k)后的灭火器使用情况    /*最优解的输出部分*/    int counter;//记录发射次数    void output(int i);  //输出矩形    int main()    {       /*输入部分*/       fin >> room_n >> shoot_k;       for(int i=2;i>=1;i--)          for(int j=1;j<=room_n;j++)             fin >> v[i][j];       for(int i=2;i>=1;i--)          for(int j=1;j<=room_n;j++)             fin >> f[i][j];       /*计算部分*/       unsigned min_loss=memo(0,0,0);       paint(0,0,0);       /*输出部分*/       for(int i=1;i<=room_n;i++) output(i);       for(int j=shoot_k-counter;j>=1;j--)          fout << "0 0 0 0\n";       return 0;    }    //    int memo(int i,int j, int k)    {       if(checked[i][j][k]) return dp[i][j][k];       if(i==room_n)  return 0;       dp[i][j][k]=INT_MAX;       for(int jj=0,kk,ans;jj<=3;jj++)  //枚举第i+1行状态       {          if(jj==3&&(j==4||j==1||j==2))  jj++; //上下都用时的状态修正          kk=k+!(jj==0||jj==j||j==4);          if(kk<=shoot_k)             ans=memo(i+1,jj,kk)+loss(i+1,jj);          else ans=INT_MAX;          if(dp[i][j][k]>ans)          {             dp[i][j][k]=ans;             build[i][j][k]=jj;          }       }       assert(dp[i][j][k]<INT_MAX);       checked[i][j][k]=true;       return dp[i][j][k];    }    int loss(int i, int j)    {    /*状态j的定义:       0:上层下层都不使用灭火器;       1:上使用;2:下使用;3,4:上下都用       其中3表示与此列相连的使用灭火器的房间最少组成1个完整矩形       4表示与此列相连的使用灭火器的房间已经最少组成2个完整矩形*/       switch(j)       {          case 0:             return (f[1][i]+f[2][i]);  //上下都不用          case 1:             return(v[2][i]+f[1][i]);   //上用          case 2:             return(v[1][i]+f[2][i]);   //下用          case 3:  //上下都用          case 4:             return(v[1][i]+v[2][i]);       }       assert(0);    }    //    void paint(int i, int j, int k)    {       if(i==room_n)  return;       assert(checked[i][j][k]);       int jj=build[i][j][k];       int kk=k+!(jj==0||jj==j||j==4);       switch(jj)       {          case 0:             break;   //上下都不用          case 1:             room[2][i+1]=1; break;  //上用          case 2:             room[1][i+1]=1; break;  //下用          case 3:          case 4:             room[1][i+1]=room[2][i+1]=true; break; //上下都用          default:             assert(0);       }       paint(i+1,jj,kk);    }    //    void output(int i)    {       counter++;       int ii;       if(room[2][i]&&!room[1][i]) //上用       {          fout << 2 << ' ' << i << ' ';          for(ii=i;ii<=room_n&&room[2][ii];ii++)  room[2][ii]=0;          fout << 2 << ' ' << ii-1 << endl;       }       else if(room[1][i]&&!room[2][i])  //下用       {          fout << 1 << ' ' << i << ' ';          for(ii=i;ii<=room_n&&room[1][ii];ii++)  room[1][ii]=0;          fout << 1 << ' ' << ii-1 << endl;       }       else if(room[1][i]&&room[2][i])   //上下都用       {          fout << 1 << ' ' << i << ' ';          for(ii=i;ii<=room_n&&room[1][ii]&&room[2][ii];ii++)  room[1][ii]=room[2][ii]=0;          fout << 2 << ' ' << ii-1 << endl;       }       else counter--;   //上下都不用    }    //

序列压缩(substract.cpp)

给出一个 N 个正整数的序列 A=[A1,A2,……,AN],我们可以对其进行压缩操作.所谓压缩
操作是指:将两个相邻的元素 AI和 AI+1的差(AI-AI+1)去替代第 I 个元素,同时删去第 I+1 个元素,严格地可以定义如下:
CON(A,I)=[A1,,A2,….,AI-1,AI-AI+1,AI+2,…….AN]
经过一次序列压缩之后,我们可以得到一个 N-1 个元素的序列,如此进行下去,我们可以仅得到单一的一个整数。如下例所示:
con([12 ,10, 4, 3, 5],2)=[12 ,6,3,5]
con([12,6,3,5],3)=[12 ,6,-2]
con([12,6,-2],2)=[12,8]
con([12,8],1)=[4]
现给定一个正整数序列和一个目标整数 T,求解并输出压缩顺序。
输入文件:
N T (1〈=N〈=100,-10000〈=T〈=10000)
A1 A2 AN
输出文件:
压缩顺序;
输入输出示例:
SUBSTRACT.IN
5 4
12 10 4 3 5
SUBSTRACT.OUT
2 3 2 1

题解:
我们可以把题目转化为在一列数中添加+ - 。
我们记所有加上 -号的数的和为S,所有数的和为sum,容易得到S=(sum-T)/2;
f[i][j]记前i个数中若干个数的和能否得到j,并记下递推路径,最后根据路径输出f[N][S]即可。

代码:

#include <cstdio>#include <cstring>#include <string>#include <algorithm>#include <iostream>using namespace std;#define Max(a,b) ((a)>(b)?(a):(b))#define Min(a,b) ((a)<(b)?(a):(b))typedef long long LL;const int maxn = 10010;int num[maxn],pre[maxn],vis[maxn];int sum,n,t,m;bool ps[maxn];string st;char str[maxn];char del (char *s,int p) {    char ch;    if(p>=strlen(s) or p<0) return 0;    ch=s[p];    for(int i=p;s[i];i++) s[i]=s[i+1];    return ch;}namespace my_self {    void init() {        scanf("%d%d",&n,&t);        for(int i=1;i<=n;i++) {            scanf("%d",&num[i]);            sum+=num[i];        }        m=(sum-t)/2;    }    void work() {        vis[num[2]]=true;pre[num[2]]=2;        for(int i=3;i<=n;i++) {            if(vis[m]) break;            for(int j=m;j>=num[i];j--) {                if(!vis[j]) {                    if(vis[j-num[i]]) {                        vis[j]=true;pre[j]=i;                    }                }            }        }    }    void print() {        for(int i=1;i<maxn;i++)            ps[i]=true;        int s=m;        while(s>0) {            ps[pre[s]]=false;s-=num[pre[s]];        }        for(int i=2;i<=n;i++) {            if(ps[i]) st=st+'+';            else st=st+'-';        }        for(int i=0;i<st.length();i++)            str[i]=st[i];        char *pch=strchr(str,'+');        while(pch!=NULL) {            printf("%d ",pch-str+1);            del(str,pch-str);            pch=strchr(str,'+');        }        for(int i=0;i<strlen(str);i++)            putchar('1'),putchar(' ');        putchar(10);    }}int main() {    freopen("substract.in","r",stdin);    freopen("substract.out","w",stdout);    my_self::init();    my_self::work();    my_self::print();    //std::cout<<st<<std::endl;    return 0;}/*5 412 10 4 3 5*/
原创粉丝点击