2016.6.10测试

1、汽艇(Boat.cpp/c/pas)
【问题描述】
有 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,取最优的情况即可。  

 1 //It is made by jump~
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <algorithm>
 8 #include <ctime>
 9 #include <vector>
10 #include <queue>
11 #include <map>
12 #include <set>
13 #ifdef WIN32   
14 #define OT "%I64d"
15 #else
16 #define OT "%lld"
17 #endif
18 using namespace std;
19 typedef long long LL;
20 const int MAXN = 1011;
21 int n,ans,cnt,jilu;
22 
23 struct node{
24     int val;
25     int fall;
26 }a[MAXN];
27 
28 struct now{
29     int val;
30 }jump[MAXN];
31 
32 inline int getint()
33 {
34        int w=0,q=0;
35        char c=getchar();
36        while((c<'0' || c>'9') && c!='-') c=getchar();
37        if (c=='-')  q=1, c=getchar();
38        while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
39        return q ? -w : w;
40 }
41 
42 inline bool cmp(node q,node qq){ return q.val>qq.val; }
43 
44 inline bool ccmp(now q,now qq){ return q.val>qq.val; }
45 
46 inline void solve(){
47     n=getint();
48 
49     for(int i=1;i<=n;i++) a[i].val=getint();
50 
51     for(int i=1;i<=n;i++) a[i].fall=getint();
52 
53     sort(a+1,a+n+1,cmp); ans=a[1].val; jilu=1;
54     for(int i=2;i<=n;i++) {
55     for(int j=1;j<=n;j++) jump[j].val=a[j].val-(i-1)*a[j].fall;
56     sort(jump+1,jump+n+1,ccmp);
57     cnt=0;
58     for(int j=1;j<=i;j++)  cnt+=jump[j].val;
59     if(cnt>=ans) {
60         ans=cnt;
61         jilu=i;
62     }
63     }
64     printf("%d
%d",ans,jilu);
65 }
66 
67 int main()
68 {
69   solve();
70   return 0;
71 }


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

【问题描述】
假设有 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

 1 //It is made by jump~
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <algorithm>
 8 #include <ctime>
 9 #include <vector>
10 #include <queue>
11 #include <map>
12 #include <set>
13 #ifdef WIN32   
14 #define OT "%I64d"
15 #else
16 #define OT "%lld"
17 #endif
18 using namespace std;
19 typedef long long LL;
20 const int MAXN = 511;
21 const int MAXT = 500011;
22 int n,tim;
23 int m[MAXN];
24 int cnt;
25 int f[MAXT];//f[i]表示i时刻(含)之前的最大值
26 
27 struct seq{
28     int l,r,val;
29 }a[MAXN*MAXN],jump[MAXN*MAXN];
30 
31 inline int getint()
32 {
33        int w=0,q=0;
34        char c=getchar();
35        while((c<'0' || c>'9') && c!='-') c=getchar();
36        if (c=='-')  q=1, c=getchar();
37        while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
38        return q ? -w : w;
39 }
40 
41 inline bool cmp(seq q,seq qq){ if(q.l==qq.l) return q.r<qq.r;  return q.l<qq.l;   }
42 
43 int main()
44 {
45   n=getint(); tim=getint();
46   for(int i=1;i<=n;i++) {
47       m[i]=getint();
48       for(int j=1;j<=m[i];j++) {
49       a[++cnt].l=getint();
50       a[cnt].r=getint();
51       a[cnt].val=getint();
52       }
53   }
54 
55   sort(a+1,a+cnt+1,cmp);
56 
57   int ji=1,nowl=a[1].l;
58   //f[a[1].r]=a[1].val;
59   for(int i=1;i<=tim;i++) {
60       f[i]=max(f[i-1],f[i]);
61       if(i<nowl) continue;
62       while(a[ji].l==nowl && ji<=cnt) f[a[ji].r]=max(f[a[ji].r],f[i-1]+a[ji].val),ji++;
63       nowl=a[ji].l;
64   }
65 
66   printf("%d",f[tim]);
67   return 0;
68 }


3、集合(gather)
【问题描述】
小明总喜欢做一些与众不同的事情,最近他迷上了集合运算。他觉得光是简单的预算不过瘾,所以他把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原题,出题人真懒。

  先贴一发大暴力:

  1 //It is made by jump~
  2 #include <iostream>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <cstdio>
  6 #include <cmath>
  7 #include <algorithm>
  8 #include <ctime>
  9 #include <vector>
 10 #include <queue>
 11 #include <map>
 12 #include <set>
 13 #ifdef WIN32   
 14 #define OT "%I64d"
 15 #else
 16 #define OT "%lld"
 17 #endif
 18 using namespace std;
 19 typedef long long LL;
 20 const int MAXN = 1000011;
 21 int n;
 22 bool pd[MAXN];
 23 int ans;
 24 
 25 struct seq{
 26     int l,r;
 27 }a[MAXN],dui[MAXN];
 28 
 29 inline int getint()
 30 {
 31        int w=0,q=0;
 32        char c=getchar();
 33        while((c<'0' || c>'9') && c!='-') c=getchar();
 34        if (c=='-')  q=1, c=getchar();
 35        while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
 36        return q ? -w : w;
 37 }
 38 
 39 inline bool cmp(seq q,seq qq){
 40     if(q.l==qq.l) return q.r<qq.r;
 41     return q.l<qq.l;
 42 }
 43 
 44 inline void check(int cnt){
 45     if(cnt==0 || cnt==1) return ;
 46     int total=0;
 47     for(int i=1;i<=n;i++) if(pd[i]) dui[++total]=a[i];
 48     sort(dui+1,dui+total+1,cmp);
 49     int head=dui[1].l,tail=dui[1].r;
 50     int maxl=0;
 51     for(int i=1;i<=total;i++) head=max(head,dui[i].l),tail=min(tail,dui[i].r);
 52     if(head>=tail) return;
 53     maxl=tail-head;
 54 
 55     int nowl=dui[1].l,nowr=dui[1].r;
 56     int ji=0; ji=nowr-nowl;
 57     for(int i=2;i<=total;i++) {
 58     if(dui[i].l==nowl) ji+=dui[i].r-nowr,nowr=dui[i].r;
 59     else{ 
 60         nowl=dui[i].l; 
 61         if(nowl>nowr)  {
 62         ji+=dui[i].r-dui[i].l; 
 63         nowr=dui[i].r;
 64         }
 65         else{
 66         if(dui[i].r>nowr) {
 67             ji+=dui[i].r-nowr;
 68             nowr=dui[i].r;
 69         }
 70         }
 71     }
 72     }
 73     maxl*=ji;
 74     if(maxl>ans) {
 75     ans=maxl;
 76     /*for(int i=1;i<=n;i++)
 77         if(pd[i])
 78         printf("%d ",i);
 79         printf("
%d 
",ans);*/
 80     }
 81 }
 82 
 83 inline void dfs(int now,int cnt){
 84     if(now==n+1) {  check(cnt);return ; }
 85     pd[now]=1; dfs(now+1,cnt+1);
 86     pd[now]=0; dfs(now+1,cnt);
 87 }
 88 
 89 inline void solve(){
 90   n=getint();
 91   for(int i=1;i<=n;i++) a[i].l=getint(),a[i].r=getint();
 92 
 93   dfs(1,0);
 94 
 95   printf("%d",ans);
 96 }
 97 
 98 int main()
 99 {
100   solve();
101   return 0;
102 }

正解:单调队列+单调栈

  1 //It is made by jump~
  2 #include <iostream>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <cstdio>
  6 #include <cmath>
  7 #include <algorithm>
  8 #include <ctime>
  9 #include <vector>
 10 #include <queue>
 11 #include <map>
 12 #include <set>
 13 #ifdef WIN32   
 14 #define OT "%I64d"
 15 #else
 16 #define OT "%lld"
 17 #endif
 18 using namespace std;
 19 typedef long long LL;
 20 const int MAXN = 1000011;
 21 int n,cnt;
 22 LL ans;
 23 
 24 struct seq{
 25     int l,r;
 26 }a[MAXN];
 27 
 28 inline LL calc(const seq &j,const seq &i){return 1LL*(i.r-j.l)*(j.r-i.l);}
 29 
 30 inline int getint()
 31 {
 32        int w=0,q=0;
 33        char c=getchar();
 34        while((c<'0' || c>'9') && c!='-') c=getchar();
 35        if (c=='-')  q=1, c=getchar();
 36        while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
 37        return q ? -w : w;
 38 }
 39 
 40 inline bool cmp(seq q,seq qq){
 41     if(q.l==qq.l) return q.r>qq.r;
 42     return q.l<qq.l;
 43 }
 44 
 45 inline LL max(LL pp,LL ppp){ if(pp<ppp) return ppp; return pp; }
 46 
 47 struct stac{
 48     int pos,top;
 49     struct data{
 50     int l,r,id;
 51     }lin,sta[MAXN];
 52     inline void init(){ sta[top=1]=(data){2,n,1}; pos=1; }
 53     bool comp(int xx,int x,int y){return calc(a[x],a[xx])<calc(a[y],a[xx]);}
 54     inline int get(int id){
 55     int l=lin.l,r=lin.r,bian=lin.id;
 56     int mid;
 57     while(l<r) {
 58         mid=(l+r)/2+1;
 59         if(comp(mid,bian,id)) l=mid; else r=mid-1;
 60     }
 61     return l;
 62     }
 63     inline void push(int id){
 64     while(top && !comp(sta[top].l,sta[top].id,id)) top--;
 65     lin=sta[top--];
 66     int m=get(id);
 67     if(lin.l<=m) sta[++top]=(data){lin.l,m,lin.id};
 68     if(m<n) sta[++top]=(data){m+1,n,id};
 69     }
 70     inline LL query(int x){
 71     while(x>sta[pos].r) pos++;
 72     return calc(a[sta[pos].id],a[x]);
 73     }
 74 }stack;
 75 
 76 struct queue{//单调队列,维护一个左端点、右端点坐标都单调递增,长度单调递减的区间队列
 77     int head,tail;
 78     struct data{
 79     int r,len;
 80     }seq[MAXN],lin;
 81     inline void push(int id){
 82     lin=(data){a[id].r,a[id].r-a[id].l};
 83     while(head<=tail && seq[tail].len<=lin.len) tail--;
 84     seq[++tail]=lin;
 85     }
 86     inline LL query(int id){
 87     int r=a[id].r;
 88     while(head<=tail && seq[head].r<r) head++;
 89     if(head<=tail) return seq[head].len;
 90     else return -1;
 91     }
 92 }que;
 93 
 94 inline void solve(){
 95     n=getint();
 96     for(int i=1;i<=n;i++) a[i].l=getint(),a[i].r=getint();
 97 
 98     sort(a+1,a+n+1,cmp);
 99     int last=0;
100     que.head=1;
101     for(int i=1;i<=n;i++) {
102     if(a[i].r>last) last=a[i].r,a[++cnt]=a[i],que.push(i);//得到去掉互相包含之后的新区间集,并加入单调队列,更新当前最右的端点值
103     else ans=max( que.query(i)*(a[i].r-a[i].l) , ans );//计算互相包含的情况
104     }
105 
106     stack.init();
107     for(int i=2;i<=cnt;i++) {
108     ans=max(ans,stack.query(i)),stack.push(i);
109     }
110 
111     printf(OT,ans);
112 }
113 
114 int main()
115 {
116   solve();
117   return 0;
118 }


4、新斯诺克(snooker.cpp/c/pas)
【问题描述】
斯诺克又称英式台球,是一种流行的台球运动。在球桌上,台面四角以及两长边中心位置各有一个球洞,使用的球分别为 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。。。高兴地发现这就是一个逆序对。。。

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

 1 //It is made by jump~
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <algorithm>
 8 #include <ctime>
 9 #include <vector>
10 #include <queue>
11 #include <map>
12 #include <set>
13 #ifdef WIN32   
14 #define OT "%I64d"
15 #else
16 #define OT "%lld"
17 #endif
18 using namespace std;
19 typedef long long LL;
20 const int MAXN = 100011;
21 int n,m;
22 int sum[MAXN],a[MAXN],jump[MAXN];
23 int g[MAXN];
24 LL ans;
25 
26 inline int getint()
27 {
28        int w=0,q=0;
29        char c=getchar();
30        while((c<'0' || c>'9') && c!='-') c=getchar();
31        if (c=='-')  q=1, c=getchar();
32        while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
33        return q ? -w : w;
34 }
35 
36 inline void merge(int l,int mid,int r){
37     int i=l,j=mid+1;
38     int cnt=l;
39     while(i<=mid && j<=r) {
40     if(jump[i]<=jump[j])  g[cnt++]=jump[i++];
41     else{
42         g[cnt++]=jump[j++];
43         //ans+=mid-i+1;
44         ans+=(LL)mid; ans-=(LL)i; ans++;
45     }
46     }
47     while(i<=mid) g[cnt++]=jump[i++];
48     while(j<=r) g[cnt++]=jump[j++];
49     //for(;i<=mid;i++) g[cnt++]=a[i];
50     //for(;j<=r;j++) g[cnt++]=a[i];
51     for(i=l;i<=r;i++) jump[i]=g[i];
52 }
53 
54 inline void gui(int l,int r){
55     if(l==r) return ;
56     int mid=(l+r)/2;
57     gui(l,mid); gui(mid+1,r);
58     merge(l,mid,r);
59 }
60 
61 inline void solve(){
62     n=getint();m=getint();
63     for(int i=1;i<=n;i++) {
64     a[i]=getint(); sum[i]=sum[i-1]+a[i];
65     jump[i]=sum[i]-m*i;
66     }
67     for(int i=1;i<=n;i++) jump[i]=-jump[i];//正序对变成逆序对
68 
69     gui(0,n);
70     
71     printf(OT,ans);
72 }
73 
74 int main()
75 {
76   solve();
77   return 0;
78 }
原文地址:https://www.cnblogs.com/ljh2000-jump/p/5573620.html