前言
突然就考试,做完1,2题就开始浪了。于是就第三题打了个暴力…
用Emacs之后代码缩进似乎就呵呵了…
操练(Training.pas/c/cpp Time:1s Memory:256M
【问题描述】
高老大有一个
N∗N
的广场,被均分成
N∗N
个格子。其中某些格子种上了树。
为了维护世界的和平,为了贯彻和平与发展的原则,老大不得不开始操练部下了。部下们必须站在广场中某没种上树个格子中,而且一个格子内不允许有两个人站着。同时,为了显得整齐划一,部下们要维护一个方阵的阵型,也就是N*N的广场内的一个
X∗Y
的矩形(该矩形的长和宽必须与广场的长和宽平行),每个格子上都必须有一个部下。现在老大想知道,一次最多操练多少个部下?
【输入】
输入文件名为Training.in。
输入第一行两个正整数N和M,代表广场的长和宽。
下接一个N行M列的字符矩阵,若第I行第J列为‘0’则代表该格子上有一颗树。
【输出】
输出文件名为Training.out。
输出一次最多能够操练的部下个数。
【输入输出样例】
Training.in
2
11
11
Training.out
4
【数据范围】
对于80%的数据,
N≤250
;
对于100%的数据,
N≤1000
。
【题解】
这道题目就是极大化子矩形 的一个模板吧,所以还好做吧。
这个题目的母题是USACO中的rectbarn,里面讲的很详细。
解释一下我这里
f[i][j]
表示的是这个点前面1的个数,那么你应该就看的懂了吧。。。
【代码】
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
const int size = 1000+10;
int n,ans;
int f[size][size];
char ch[size][size];
int minn,cnt;
inline int read() {
int f=1,in=0;
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 main() {
freopen("training.in","r",stdin);
freopen("training.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
scanf("%s",ch[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(ch[i][j]=='1') f[i][j]=f[i][j-1]+1;
/*for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++)
printf("%d ",f[i][j]);
puts("");
}*/
for(int i=n;i>=1;i--)
for(int j=1;j<=n;j++) {
minn=f[j][i];
cnt=1;
if(minn*(n-j+1)<=ans) continue;
ans=Max(ans,cnt*minn);
for(int k=j+1;k<=n;k++) {
minn=Min(minn,f[k][i]);
cnt++;
if(minn*(n-j+1)<=ans) break;
ans=Max(ans,cnt*minn);
}
}
printf("%d\n",ans);
return 0;
}
炸弹(Bomb.pas/c/cpp Time:1s Memory:256M)
【问题描述】
高老大又在鼓捣炸弹了,这不,启动了一个了。英明英勇的老大又在跟他的部下们表演空手光速拆炸弹了。
炸弹上是一个圆盘,圆盘上顺时针写着N个数。其实这个炸弹就是要求从中选择若干个连续的数(注意每个数最多只能选一次)加起来,使得这些数字的和最大,然后输入这个
最大的和,计时就会停止。现在你被老大要求上台表演,时间是1s,任务就交给你了。
【输入】
输入文件名为Bomb.in。
输入第一行包含一个正整数N,表示数字的个数。
第二行包含N个整数,为所给的数字。
【输出】
输出文件名为Bomb.out。
输出包含一个整数,为最大的可以得到的和。
【输入输出样例】
Bomb.in
8
2 -4 6 -1 -4 8 -1 3
Bomb.out
14
【数据范围】
对于30%的数据
1≤N≤200
;
对于70%的数据
1≤N≤10000
;
对于100%的数据
1≤N≤100000
, 答案在longint范围内。
【题解】
这个题目可以很明显的发现就是求最大连续子序列的题目
然后我们可以一般轻松的求出在非环上的最大连续子序列,
O(n)
出界
然后就是处理此时在环上的情况了。。。
我们将一个方案分成两部分,一部分从 1 开始递增,一部分从 n 开始递减。
然后用dp就可以求出末端不超过 i 且从 1 或 n 出发的最大连续子序列。
然后就没有然后了…
【代码】
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
typedef int LL;
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
const int size = 200000+10;
LL n,ans;
LL a[size],f[size];
inline LL read() {
LL f=1,in=0;
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;
}
inline LL erfen(LL l,LL r) {
if(l==r) return f[l];
LL mid=(l+r)>>1;
return Min(erfen(l,mid),erfen(mid+1,r));
}
int main() {
freopen("bomb.in","r",stdin);
freopen("bomb.out","w",stdout);
n=read();
for(LL i=1;i<=n;i++)
a[i]=a[i+n]=read();
LL m=n<<1;
for(LL i=1;i<=m;i++)
f[i]=f[i-1]+a[i];
LL minn=0;
for(LL i=n;i<=m;i++) {
if(f[i-n-1]==minn) minn=erfen(i-n,i);
else minn=Min(minn,f[i]);
ans=Max(ans,f[i]-minn);
}
printf("%d\n",ans);
return 0;
}
战争(War.pas/c/cpp Time:1s Memory:256M)
【问题描述】
高老大要打团战了。他要召集N(N是奇数)个人去组织一场战争。现在高老大的手下有T个人,每个人都有一个战斗力值和影响力值。当这N个人的影响力值之和超过M的时候,必然会引起巨大的社会动荡。为了爱与和平,老大明智地决定,不引起巨大的社会动荡。同时,英明的老大发现,一个团队整体能力等于这N个人的战斗力的中位数,中位数越高则战斗力越强。现在老大想知道,这N个人的团队整体能力最大为多少。
【输入】
输入文件名为War.in。
输入第一行为三个正整数N,T,M,意义如上述。
后接T行,每行两个正整数Wi和Vi,代表每个人的战斗力值和影响力值。
【输出】
输出文件名为War.out。
输出一行一个整数,代表这N个人的团队最大的整体能力。无解输出-1。
【输入输出样例】
War.in
3 5 70
30 25
50 21
20 20
5 18
35 30
War.out
35
【样例解释】
选第2、4、5个人,影响力21+18+30=69<=70,同时该团队的整体能力为最高的35。
【数据范围】
对于30%的数据,保证
T≤200
。
对于100%的数据,保证
T≤100000
,
N≤20000
每个人的影响力值
≤100000
,
M≤231−1
.
【题解】
我当时一看这个题目,然后就开始准备打暴力,不怎么想去想,然后打完暴力30分,再然后就…没有然后了…
考后据说就是把每个人按照战斗力值进行排序,那么枚举中位数,对于第 i 个人,我们只需要求出
1→i−1
中影响力值最小的
n2
个人之和,
i+1→n
中影响力值最大的
n2
个人之和。
若它们之和再加上当前人的影响力值小于等于 m,就可以更新最大值。
在这之后就得要求区间前K大的数的和了。
只要建一个大根堆,每次比较当前数字与堆顶元素的大小关系,若比堆顶元素大,则不管;若比堆顶元素小,则取出堆顶元素,放入当前数字,然后更新和。
时间复杂度
O(nLogn)
【代码】
先纪念我的30分暴力代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxx = 200+10;
int n,t,m,ans=0;
bool yes;
int a[maxx],b[maxx],num[maxx];
struct people {
int c,w;
}ren[maxx];
bool cmp(const people &x,const people &y) {
return x.c<y.c;
}
void dfs(int k,int sum) {
int l=t-(n-k);
for(int i=a[k-1]+1;i<=l;i++)
if(sum+ren[i].w<=m) {
a[k]=i;
b[k]=ren[i].c;
if(k==n) {
yes=true;
if(k&1)
ans=max(ans,b[(k+1)/2]*2);
else
ans=max(ans,b[k/2]+b[k/2+1]);
}
else
dfs(k+1,sum+ren[i].w);
a[k]=0;b[k]=0;
}
}
int main() {
freopen("war.in","r",stdin);
freopen("war.out","w",stdout);
scanf("%d%d%d",&n,&t,&m);
if(t>25)
return 0;
for(int i=1;i<=t;i++)
scanf("%d%d",&ren[i].c,&ren[i].w);
sort(ren+1,ren+1+t,cmp);
if(t<=25)
dfs(1,0);
if(!yes)
puts("-1");
else {
if(ans&1)
printf("%.1lf\n",(double)ans/2.0);
else
printf("%d\n",ans/2);
}
return 0;
}
然后这是AC代码
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
inline int read() {
int in=0,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;
}
const int size = 100000+10;
struct Node {
int w,c,num;
bool operator > (const Node &A) const {
return A.c<c;
}
bool operator < (const Node &A) const {
return A.c>c;
}
}node[size];
inline bool cmp(const Node &a,const Node &b) {
return a.w<b.w;
}
int n,t;
LL m;
bool use[size];
priority_queue<Node ,vector<Node>,greater<Node> > q1;
priority_queue<Node ,vector<Node>,greater<Node> > q2;
priority_queue<Node ,vector<Node>,less<Node> > q3;
int main() {
freopen("war.in","r",stdin);
freopen("war.out","w",stdout);
n=read();t=read();m=read();
for(int i=1;i<=t;i++)
node[i].w=read(),node[i].c=read();
sort(node+1,node+t+1,cmp);
int k=n>>1;
LL sum1=0,sum2=0;
Node Top;
for(int i=1;i<=t;i++)
node[i].num=i;
for(int i=t-k-1;i>=1;i--)
q1.push(node[i]);
for(int i=t;i>t-k;i--)
q2.push(node[i]);
for(int i=1;i<=k;i++) {
Top=q1.top();q1.pop();
sum1+=Top.c;
use[Top.num]=true;
}
for(int i=1;i<=k;i++) {
Top=q2.top();
q2.pop();
sum2+=Top.c;
q3.push(Top);
}
for(int i=t-k;i>k;i--) {
if(use[i]) {
sum1-=node[i].c;
Top=q1.top();
while(use[Top.num]) {
q1.pop();
Top=q1.top();
}
sum1+=Top.c;
use[Top.num]=true;
q1.pop();
}
else use[i]=true;
if(sum1+sum2+node[i].c<=m) {
printf("%d\n",node[i].w);
return 0;
}
Top=q3.top();q3.pop();
sum2-=Top.c;q2.push(Top);
q2.push(node[i]);
Top=q2.top();q2.pop();
sum2+=Top.c;q3.push(Top);
}
puts("-1");
return 0;
}
总结
还得继续想,dp水平才上的去啊,暴力少打点,多想些吧
最后%%%Yzy大神%%% 即将AK

本文通过三道编程题目,介绍了动态规划在解决实际问题中的应用。包括操练(Training)问题,求解最大操练人数的子矩阵;炸弹(Bomb)问题,寻找最大连续子序列和;战争(War)问题,找到最高战斗力的团队。文章提供问题描述、输入输出样例、数据范围,并给出了解题思路和关键代码实现。

被折叠的 条评论
为什么被折叠?



