您的位置:首页 > 其它

2016.6.10测试

2016-06-10 14:01 197 查看
[b]1、汽艇(Boat.cpp/c/pas)[/b]
【问题描述】
有 n 个人要去坐 1 汽艇,每个人单独坐汽艇的快乐程度是 Ci,每多一个人,他的快乐程度会减去 Di,请求出使快乐程度之和达到最大的方案。(假设汽艇的容量足够大)。
【输入格式】
输入文件共有 3 行:
第1 行是一个整数 n;
第2 行有n 个整数,依次表示每个人单独坐汽艇的快乐程度 Ci(1<=Ci<=10000);
第3 行有n 个整数,依次表示每多 1 人,每个人快乐程度的下降值 Di(1<=Di<=10)。
【输出格式】
应输出两行:
第1 行一个整数,是最大的快乐程度之和;
第2 行一个整数,是最大的快乐程度之和所对应的汽艇上的人数(若有多种方案,则输出人数最多的)。
【输入样例】
6
10 10 10 10 10 9
2 2 2 2 2 3
【输出样例】
18
3
【样例说明】
前 3 个人去坐汽艇可使快乐程度之和达到最大,每个人的快乐程度均为 10-2*2=6,总和是
18。
【数据范围】
对于 30%的数据,n<=20;
对于 100%的数据,n<=1000。

正解:贪心

解题报告:

  标准水题,枚举有多少个人,然后每次sort,取最优的情况即可。  

//It is made by jump~
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#ifdef WIN32
#define OT "%I64d"
#else
#define OT "%lld"
#endif
using namespace std;
typedef long long LL;
const int MAXN = 1011;
int n,ans,cnt,jilu;

struct node{
int val;
int fall;
}a[MAXN];

struct now{
int val;
}jump[MAXN];

inline int getint()
{
int w=0,q=0;
char c=getchar();
while((c<'0' || c>'9') && c!='-') c=getchar();
if (c=='-')  q=1, c=getchar();
while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
return q ? -w : w;
}

inline bool cmp(node q,node qq){ return q.val>qq.val; }

inline bool ccmp(now q,now qq){ return q.val>qq.val; }

inline void solve(){
n=getint();

for(int i=1;i<=n;i++) a[i].val=getint();

for(int i=1;i<=n;i++) a[i].fall=getint();

sort(a+1,a+n+1,cmp); ans=a[1].val; jilu=1;
for(int i=2;i<=n;i++) {
for(int j=1;j<=n;j++) jump[j].val=a[j].val-(i-1)*a[j].fall;
sort(jump+1,jump+n+1,ccmp);
cnt=0;
for(int j=1;j<=i;j++)  cnt+=jump[j].val;
if(cnt>=ans) {
ans=cnt;
jilu=i;
}
}
printf("%d\n%d",ans,jilu);
}

int main()
{
solve();
return 0;
}


[b]2、上网(net.cpp/c/pas) [/b]

【问题描述】
假设有 n 个人要上网,却只有 1 台电脑可以上网。上网的时间是从 1 szw 至 T szw ,szw是一个自创的时间单位,至于 szw怎么换算成 s,min 或 h,没有人清楚。依次给出每个人在某个时间段内上网的快乐程度 C(必须这个人在整个时间段内都在上网,才能获得快乐程度C,否则,快乐程度是 0),请你得到使总的快乐程度达到最大的方案。
【输入格式】
第1 行2 个整数 n和 T,含义如题目所述;
接下来有 n 个这样的结构(每两个相邻的结构之间有一空行,且第 1 个结构和第一行间有
一空行):
第1 行一个整数 Mi,表示第 i 个人的时间段的个数;
接下来有 Mi 行,每行3个整数 Xj,Yj,C,表示第 i个人在[Xj,Yj]内上网的快乐程度为 C,
因此有 Xj-Yj-1=1,X1=1,Ymi=T,Xj<=Yj。
【输出格式】
仅输出一行,为总的最大的快乐程度。
【输入样例】
3 10
3
1 3 6
4 7 9
8 10 3
3
1 3 5
4 7 10
8 10 1
4
1 3 2
4 8 2
9 9 6
10 10 3
【输出样例】
25
【样例说明】
在[1,3]内,安排 1 上网,快乐程度为 6;
在[4,7]内,安排 2 上网,快乐程度为 10; 在[8,8]内,不安排;
在[9,9]内,安排 3 上网,快乐程度为 6;
在[10,10]内,安排 3 上网,快乐程度为 3;
这是使总的快乐程度达到最大的方案,对应的值是 25。
【数据范围】
对于 30%的数据,n<=4,所有的 Mi<=5,T<=20;
对于 60%的数据,n<=100,所有的 Mi<=100,T<=2000;
对于 100%的数据,n<=500,所有的 Mi<=500,T<=500000,所有的 0<C<=10^9,并保证最终解 Max<=10^9。

正解:DP

解题报告:

  这道题我开始居然想了很久,我真是太菜了。。。

  果断DP,每个人的各段区间没有任何区别,直接丢一起按照左端点排序。题目可转化为给定若干带权区间,取出不重叠的区间使权值最大。

  貌似大家都是在做区间数的做法。。。我是直接讨论时间,for时间,然后记录一下处理到哪段区间,并且当前的最左端点,一维DP

//It is made by jump~
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#ifdef WIN32
#define OT "%I64d"
#else
#define OT "%lld"
#endif
using namespace std;
typedef long long LL;
const int MAXN = 511;
const int MAXT = 500011;
int n,tim;
int m[MAXN];
int cnt;
int f[MAXT];//f[i]表示i时刻(含)之前的最大值

struct seq{
int l,r,val;
}a[MAXN*MAXN],jump[MAXN*MAXN];

inline int getint()
{
int w=0,q=0;
char c=getchar();
while((c<'0' || c>'9') && c!='-') c=getchar();
if (c=='-')  q=1, c=getchar();
while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
return q ? -w : w;
}

inline bool cmp(seq q,seq qq){ if(q.l==qq.l) return q.r<qq.r;  return q.l<qq.l;   }

int main()
{
n=getint(); tim=getint();
for(int i=1;i<=n;i++) {
m[i]=getint();
for(int j=1;j<=m[i];j++) {
a[++cnt].l=getint();
a[cnt].r=getint();
a[cnt].val=getint();
}
}

sort(a+1,a+cnt+1,cmp);

int ji=1,nowl=a[1].l;
//f[a[1].r]=a[1].val;
for(int i=1;i<=tim;i++) {
f[i]=max(f[i-1],f[i]);
if(i<nowl) continue;
while(a[ji].l==nowl && ji<=cnt) f[a[ji].r]=max(f[a[ji].r],f[i-1]+a[ji].val),ji++;
nowl=a[ji].l;
}

printf("%d",f[tim]);
return 0;
}


[b]3、集合(gather)[/b]
【问题描述】
小明总喜欢做一些与众不同的事情,最近他迷上了集合运算。他觉得光是简单的预算不过瘾,所以他把2个集合运算集合起来。由于小明很喜欢区间运算。于是他定义了一个这样的值。
你N个各不相同的区间,请你从中找出若干个区间使其权值最大。
W=|A1∪A2∪……∪AK|*|A1∩A2∩……AK|
其中{A1,A2……AK}(K>1,Ai<>Aj{i<>j})
如果这些区间没有交集则权值为0,给你N个各不相同的区间,请你从中找出若干个区间使其权值最大,并告诉小明。
【输入文件】
第一行N
接下来N行 l r(1<=l<r<=10^6)
【输出文件】
最大权值
【输入样例】
4
1 6
4 8
2 7
3 5
【输出样例】
24
【数据约定】
10% N<=10
30% N<=2*10^4
50% N<=10^5
100% 1<N<=10^6

正解:决策单调性

解题报告:

  考场上明明想出来了,一定是两个的时候最优,但是自己不相信自己,而且感觉n^2的暴力也会TLE就没打,打了一个大暴力,水到20分。

  等我打完大暴力,心血来潮make数据发现永远是2个的时候最优。。。追悔莫及。。。

  显然直接两两枚举会TLE,就要考虑优化。

  题解说 用决策单调性,有一些奇奇怪怪的性质。传送门:http://www.cnblogs.com/chenyushuo/p/5261657.html

  这道题居然是BZOJ原题,出题人真懒。

  先贴一发大暴力:

//It is made by jump~
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#ifdef WIN32
#define OT "%I64d"
#else
#define OT "%lld"
#endif
using namespace std;
typedef long long LL;
const int MAXN = 1000011;
int n;
bool pd[MAXN];
int ans;

struct seq{
int l,r;
}a[MAXN],dui[MAXN];

inline int getint()
{
int w=0,q=0;
char c=getchar();
while((c<'0' || c>'9') && c!='-') c=getchar();
if (c=='-')  q=1, c=getchar();
while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
return q ? -w : w;
}

inline bool cmp(seq q,seq qq){
if(q.l==qq.l) return q.r<qq.r;
return q.l<qq.l;
}

inline void check(int cnt){
if(cnt==0 || cnt==1) return ;
int total=0;
for(int i=1;i<=n;i++) if(pd[i]) dui[++total]=a[i];
sort(dui+1,dui+total+1,cmp);
int head=dui[1].l,tail=dui[1].r;
int maxl=0;
for(int i=1;i<=total;i++) head=max(head,dui[i].l),tail=min(tail,dui[i].r);
if(head>=tail) return;
maxl=tail-head;

int nowl=dui[1].l,nowr=dui[1].r;
int ji=0; ji=nowr-nowl;
for(int i=2;i<=total;i++) {
if(dui[i].l==nowl) ji+=dui[i].r-nowr,nowr=dui[i].r;
else{
nowl=dui[i].l;
if(nowl>nowr)  {
ji+=dui[i].r-dui[i].l;
nowr=dui[i].r;
}
else{
if(dui[i].r>nowr) {
ji+=dui[i].r-nowr;
nowr=dui[i].r;
}
}
}
}
maxl*=ji;
if(maxl>ans) {
ans=maxl;
/*for(int i=1;i<=n;i++)
if(pd[i])
printf("%d ",i);
printf("\n%d \n",ans);*/
}
}

inline void dfs(int now,int cnt){
if(now==n+1) {  check(cnt);return ; }
pd[now]=1; dfs(now+1,cnt+1);
pd[now]=0; dfs(now+1,cnt);
}

inline void solve(){
n=getint();
for(int i=1;i<=n;i++) a[i].l=getint(),a[i].r=getint();

dfs(1,0);

printf("%d",ans);
}

int main()
{
solve();
return 0;
}


正解:单调队列+单调栈

//It is made by jump~
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#ifdef WIN32
#define OT "%I64d"
#else
#define OT "%lld"
#endif
using namespace std;
typedef long long LL;
const int MAXN = 1000011;
int n,cnt;
LL ans;

struct seq{
int l,r;
}a[MAXN];

inline LL calc(const seq &j,const seq &i){return 1LL*(i.r-j.l)*(j.r-i.l);}

inline int getint()
{
int w=0,q=0;
char c=getchar();
while((c<'0' || c>'9') && c!='-') c=getchar();
if (c=='-')  q=1, c=getchar();
while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
return q ? -w : w;
}

inline bool cmp(seq q,seq qq){
if(q.l==qq.l) return q.r>qq.r;
return q.l<qq.l;
}

inline LL max(LL pp,LL ppp){ if(pp<ppp) return ppp; return pp; }

struct stac{
int pos,top;
struct data{
int l,r,id;
}lin,sta[MAXN];
inline void init(){ sta[top=1]=(data){2,n,1}; pos=1; }
bool comp(int xx,int x,int y){return calc(a[x],a[xx])<calc(a[y],a[xx]);}
inline int get(int id){
int l=lin.l,r=lin.r,bian=lin.id;
int mid;
while(l<r) {
mid=(l+r)/2+1;
if(comp(mid,bian,id)) l=mid; else r=mid-1;
}
return l;
}
inline void push(int id){
while(top && !comp(sta[top].l,sta[top].id,id)) top--;
lin=sta[top--];
int m=get(id);
if(lin.l<=m) sta[++top]=(data){lin.l,m,lin.id};
if(m<n) sta[++top]=(data){m+1,n,id};
}
inline LL query(int x){
while(x>sta[pos].r) pos++;
return calc(a[sta[pos].id],a[x]);
}
}stack;

struct queue{//单调队列,维护一个左端点、右端点坐标都单调递增,长度单调递减的区间队列
int head,tail;
struct data{
int r,len;
}seq[MAXN],lin;
inline void push(int id){
lin=(data){a[id].r,a[id].r-a[id].l};
while(head<=tail && seq[tail].len<=lin.len) tail--;
seq[++tail]=lin;
}
inline LL query(int id){
int r=a[id].r;
while(head<=tail && seq[head].r<r) head++;
if(head<=tail) return seq[head].len;
else return -1;
}
}que;

inline void solve(){
n=getint();
for(int i=1;i<=n;i++) a[i].l=getint(),a[i].r=getint();

sort(a+1,a+n+1,cmp);
int last=0;
que.head=1;
for(int i=1;i<=n;i++) {
if(a[i].r>last) last=a[i].r,a[++cnt]=a[i],que.push(i);//得到去掉互相包含之后的新区间集,并加入单调队列,更新当前最右的端点值
else ans=max( que.query(i)*(a[i].r-a[i].l) , ans );//计算互相包含的情况
}

stack.init();
for(int i=2;i<=cnt;i++) {
ans=max(ans,stack.query(i)),stack.push(i);
}

printf(OT,ans);
}

int main()
{
solve();
return 0;
}


[b]4、新斯诺克(snooker.cpp/c/pas)[/b]
【问题描述】
斯诺克又称英式台球,是一种流行的台球运动。在球桌上,台面四角以及两长边中心位置各有一个球洞,使用的球分别为 1 个白球,15 个红球和 6 个彩球(黄、绿、棕、蓝、粉红、黑)共22个球。击球顺序为一个红球、一个彩球直到红球全部落袋,然后以黄、绿、棕、蓝、粉红、黑的顺序逐个击球,最后以得分高者为胜。斯诺克的魅力还在于可以打防守球, 可以制造一些障碍球使对方无法击打目标球而被扣分。正是因为这样,斯诺克是一项充满神奇的运动。 现在考虑这样一种新斯诺克,设母球(母球即是白球,用于击打其他球)的标号为M,台面上有 N 个红球排成一排,每一个红球都有一个标号,`他们的标号代表了他们的分数。 现在用母球击打这些红球,一杆击打,如果母球接触到红球,就称为“K 到红球”。我们假设,一次可以击打任意多相邻连续的红球,也可以只击打一个球。并且红球既不会落袋,也不会相互发生碰撞,而只是停留在原处。每次击打时候,要想“K到红球”,至少要击打一个红球,如果想一次击打多个红球,那么击打的红球必须是依次连续排列的。 如果一次 “K 到红球”所有红球的标号之和的平均数大于母球的标号M,就获得了一个 “连击”。 现在请你计算总共能有多少种“连击”方案。
注意:如果当前有标号为 1、2、3的三种红球,母球标号为 0, 有如下 6 种获得“连击”方案: (1) 、 (2) 、 (3) 、 (1,2) 、 (2,3) 、 (1,2,3)
【输入文件】
输入文件共有两行,第一行是 N,M (N<=100000,M<=10000),
N 表示台面上一共有 N 个红球,M 表示母球的标号。
第二行是 N 个正整数,依次表示台面上 N 个红球的标号,所有标号均不超过10000。
【输出文件】
输出文件只有一个数,为“连击”的方案总数。
【输入样例】
4 3
3 7 2 4
【输出样例】
7

正解:归并排序,逆序对

解题报告:

  刚看到这道题就想到POJ上刷的那道求序列的最大平均值的神题,那个是用斜率优化、凸包优化的DP,结果发现跟这道题并没有关系,被小胖犇狠狠嘲讽了一波。

  静下心来推导了一波。发现一个神奇的性质:一个区间[i,j]满足条件当且仅当(前缀和)(sum[j]-sum[i-1])/(j-i) > m,移项:(sum[j]-sum[i-1])>(j-i)*m。。。高兴地发现这就是一个逆序对。。。

  直接归并排序,求前缀和的逆序对。

//It is made by jump~
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#ifdef WIN32
#define OT "%I64d"
#else
#define OT "%lld"
#endif
using namespace std;
typedef long long LL;
const int MAXN = 100011;
int n,m;
int sum[MAXN],a[MAXN],jump[MAXN];
int g[MAXN];
LL ans;

inline int getint()
{
int w=0,q=0;
char c=getchar();
while((c<'0' || c>'9') && c!='-') c=getchar();
if (c=='-')  q=1, c=getchar();
while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
return q ? -w : w;
}

inline void merge(int l,int mid,int r){
int i=l,j=mid+1;
int cnt=l;
while(i<=mid && j<=r) {
if(jump[i]<=jump[j])  g[cnt++]=jump[i++];
else{
g[cnt++]=jump[j++];
//ans+=mid-i+1;
ans+=(LL)mid; ans-=(LL)i; ans++;
}
}
while(i<=mid) g[cnt++]=jump[i++];
while(j<=r) g[cnt++]=jump[j++];
//for(;i<=mid;i++) g[cnt++]=a[i];
//for(;j<=r;j++) g[cnt++]=a[i];
for(i=l;i<=r;i++) jump[i]=g[i];
}

inline void gui(int l,int r){
if(l==r) return ;
int mid=(l+r)/2;
gui(l,mid); gui(mid+1,r);
merge(l,mid,r);
}

inline void solve(){
n=getint();m=getint();
for(int i=1;i<=n;i++) {
a[i]=getint(); sum[i]=sum[i-1]+a[i];
jump[i]=sum[i]-m*i;
}
for(int i=1;i<=n;i++) jump[i]=-jump[i];//正序对变成逆序对

gui(0,n);

printf(OT,ans);
}

int main()
{
solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: