前言
由于自己的一些年级组那边的事情影响了当时考试时的心情,然后就A了第一题就没什么心思去搞其他题目了,这是很不好的。
题目
火 车 票(ticket.cpp)
Timelimit :0.1s Name :Ticket
一个铁路线上有 n(2<=n<=10000)个火车站,每个火车站到该线路的首发火车站距离都是已知的。任意两站之间的票价如下表所示:
站之间的距离 – X 票价 0≤X≤L1 C1 L1≤X≤L2 C2 L2≤X≤L3 C3 其中 L1,L2,L3,C1,C2,C3 都是已知的正整数,且( 1≤L1<L2<L3≤109 , 1≤C1≤C2≤C3≤109 )。显然若两站之间的距离大于 L3 ,那么从一站到另一站至少要买两张
票。注意:每一张票在使用时只能从一站开始到另一站结束。
任务
对于给定的线路,求出从该线路上的站 A 到站 B 的最少票价。
输入
输入文件的第一行为 6 个整数, L1,L2,L3,C1,C2,C3(1≤L1≤L2≤L3≤109,1≤C1≤C2≤C3≤109) ,这些整数由空格隔开.第二行为火车站的数量 N ( 2≤N≤10000 ).
第三行为两个不同的整数 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来表示的话,那么问题的目标就是使
∑e∈SVe+∑e∉SFe
这个值最小。
因为
∑e∈SFe+∑e∉SFe=Sum(F)
(Sum(F)表示F矩阵中的元素总和),所以上面的式子可以化为
∑e∈SVe+(Sum(F)−∑e∈SFe)
,而Sum(F)又是个常数,所以实际上问题的目标就是使
∑e∈S(Ve−Fe)
最小。
下面是规划方程:
f(s,i,j)=min⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪f(s,i−1,j)f(s,i,j−1)f(s−1,t−1,j)+sum(1,t,1,i)(1≤t≤i)f(s−1,i,t−1)+sum(2,t,2,j)(1≤t≤j)f(s−1,t−1,t−1)+sum(1,t,2,min(i,j))(1≤t≤min(i,j))
边界条件
f(0,i,j)=0(0≤i,j≤N)
f(s,0,0)=0(0≤s≤k)
然后输出就得在状态转移时记录即可。
代码:
#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 4
12 10 4 3 5
*/