2017.3.25NOIP模拟测试

1. 简单的数列
【问题描述】
一个简单的数列问题:
给定一个长度为 n 的数列,求数列中这样的三个元素 ai,aj,ak 的个数,满足 ai< aj > ak,
且 i<j<k 。
【输入】
第 1 行是一个整数 n(1<=n<=50000)

接下来 n 行,每行一个元素 ai(0 <= ai <= 32767)。
【输出】
一个数,满足 ai<aj>ak (i<j<k) 的个数。
【输入输出样例】
Input
5
1
2
3
4
1
Output
6
【数据范围】
对于 30%的输入数据有 n<=2000。
对于 80%的输入数据有 n<=10000。
对于 100%的输入数据有 n<=50000。

 

 

第一题,树状数组板子题

 1 #define ll long long
 2 #include<algorithm>
 3 #include<iostream>
 4 #include<iomanip>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cstdio>
 8 #include<queue>
 9 #include<ctime>
10 #include<cmath>
11 #include<stack>
12 #include<map>
13 #include<set>
14 
15 using namespace std;
16 const int maxn=50010;
17 int C1[maxn],C2[maxn];
18 int a[maxn],b[maxn];
19 int c1[maxn],c2[maxn];
20 int n;
21 void add1(int p,int x) {
22     while(p<=32768) {
23         C1[p]++;
24         p+= (p & (-p));
25     }
26 }
27 void add2(int p,int x) {
28     while(p<=32768) {
29         C2[p]++;
30         p+=(p & (-p));
31     }
32 }
33 int getsum1(int p) {
34     int sum=0;
35     while(p) {
36         sum+=C1[p];
37         p-=(p & (-p));
38     }
39     return sum;
40 }
41 int getsum2(int p) {
42     int sum=0;
43     while(p) {
44         sum += C2[p];
45         p-=(p & (-p));
46     }
47     return sum;
48 }
49 int main() {
50     freopen("queueb.in","r",stdin);
51      freopen("queueb.out","w",stdout);
52     cin>>n;
53     int i;
54     for(i=1; i<=n; i++) scanf("%d",&a[i]),a[i]++;
55     for(i=1; i<=n; i++) b[i]=a[n-i+1];
56     for(i=1; i<=n; i++) {
57         add1(a[i],1);
58         int p=a[i]-1;
59         if(!p) continue;
60         c1[i]=getsum1(p);
61     }
62     for(i=1; i<=n; i++) {
63         add2(b[i],1);
64         int p=b[i]-1;
65         if(!p) continue;
66         c2[i]=getsum2(p);
67     }
68     int ll sum=0;
69     for(i=1; i<=n; i++) {
70         sum += (ll) c1[i] * (ll) c2[n-i+1];
71     }
72     cout<<sum;
73     return 0;
74 }
View Code

 


- 2 -2. 黄金矿工
【问题描述】
黄金矿工是一个经典的小游戏,它可以锻炼人的反应能力。该游戏中,可以通过“挖矿”
获得积分并不断升级。玩家可以在线玩 flash 版黄金矿工,也可以下载后玩单机版黄金矿工。
目前,黄金矿工小游戏有多个版本,例如黄金矿工双人版,黄金矿工单人版等。
Jimmy 是一位黄金矿工,他所在的金矿是一个 n*n 的矩形区域(俯视),区域内有黄金、
石头和 TNT,由一个 n*n 的矩阵描述。黄金的价值对应矩阵中的正值,石头的价值对应矩阵
中的负值,TNT 由 0 表示。换句话说,挖到黄金赚钱,石头亏损,如果挖到 TNT 就挂了。
Jimmy 租到的挖矿工具很特别,它的形状是一个长宽任意(均为正整数)的矩形,可以
取走被该工具覆盖的矩形区域内的所有物品,但如果该区域内有 TNT,该工具将被炸毁,此
时 Jimmy 将不得不赔偿矿主+∞元!!
!需要注意的是,该工具只能在金矿范围内使用(即不
得超出金矿边界),且租金为每次使用十元。
现在,Jimmy 想知道,如果他至多只有一次租用该工具的机会,他能获得的最大收益是
多少。当然,如果 Jimmy 租用该工具无论如何都会亏损,他可以不租用,此时收益为 0.
【输入】
第一行:一个整数 n
接下来 n 行,每行 n 个整数(绝对值<100)
,为题目中所描述的矩阵。
【输出】
一个数,即 Jimmy 所能获得的最大收益。
【输入输出样例 1】
Input
3
0 -1 -1
0 -12 0
-19 0 0
Output
0
【样例解释】
无论 Jimmy 怎么挖矿,挖到的不是石头,就是 TNT,总之无论如何都会亏损,所以选择不租
用工具,收益为 0
【数据范围】
对于 30%的数据:0<n<=10
对于 60%的数据:0<n<=100
对于 100%的数据:0<n<=300

 

 

第二题,考场上没能写出来,。。。其实这个题要用到决策性优化,如果只有一列,则最大的值这样表示,f[i]表示以i结尾的 值最大的长条,设s[i]为前缀和,则f[i]=max(s[i]-s[k])(k<i),即找到一个最小的k,这是一个决策。那么对于i来说,i 的前面最小的也是在k的时候取得最小,是一样的,所以i的决策就是i-1的决策,如果s[i] < s[k],则把k置为i。回到这题,只是把一维变成了二维,使用二位前缀和,相当于还是一样的,只要枚举一下长度,矩阵的坐标,O(n^3);

 

 1 #define ll long long
 2 #include<algorithm>
 3 #include<iostream>
 4 #include<iomanip>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cstdio>
 8 #include<queue>
 9 #include<ctime>
10 #include<cmath>
11 #include<stack>
12 #include<map>
13 #include<set>
14 #define SS system("pause");
15 #define inf 3000000
16 using namespace std;
17 const int N=310;
18 int a[N][N],h[N][N];
19 int n,ans=0;
20 void go_30() {
21     int i,j,k; 
22     for(int len=1;len<=n;len++) {
23     for(i=n;i>=len;i--){
24         k=0;
25         for(j=1;j<=n;j++){
26         int op=h[i][k]-h[i-len][k];
27         int now=h[i][j]-h[i-len][j];
28         if(now<op) k=j; 
29         else ans=max(ans,now-op);
30         }
31     }
32     }
33 }
34 int main() {
35     freopen("miner.in","r",stdin);
36              freopen("miner.out","w",stdout);
37     cin>>n;int i,j;
38     for(i=1;i<=n;i++)
39       for(j=1;j<=n;j++) {
40         scanf("%d",&a[i][j]);
41         if(!a[i][j]) a[i][j]=-inf;
42         h[i][j] = h[i][j-1]+a[i][j];
43       }
44     for(i=2;i<=n;i++) 
45       for(j=1;j<=n;j++)
46         h[i][j] += h[i-1][j];
47     go_30(); 
48     if(ans<=10) cout<<0;
49     else cout<<ans-10;
50     return 0;
51 }
View Code

 


- 3 -3. 旅行
【问题描述】
Z 小镇是一个景色宜人的地方,吸引来自各地观光客来此旅游观光。Z 小镇附近共有 N
个景点(编号为 1,2,3...N)
,这些景点被 M 条道路连接着,所有道路都是双向的,两个
景点之间可能有多条道路连接着。
也许是为了保护该地的旅游资源,Z 小镇有个奇怪的规定,就是对于一条给定的公路
Ri,任何在该公路上行驶的车辆速度必须为 Vi。速度变化太快使得游客们很不舒服,因此
从一个景点前往另一个景点的时候,大家都希望选择行使过程中最大速度和最小速度的比尽
可能小的路线,也就是所谓最舒适路线。
【输入】
第一行包括两个整数: N 和 M
接下来的 M 行每行包含三个正整数: x,y 和 v。表示景点 x 到景点 y 之间有一条双向
公路,车辆必须以速度 v 在该公路上行驶。
最后一行包含两个正整数 s,t,
表示想知道从景点 s 到景点 t 最大最小速度比最小的路径。
s 和 t 不可能相同。
【输出】
如果景点 s 到景点 t 没有路径,输出“IMPOSSIBLE”
。否则输出一个数,表示最小的速
度比。如果需要,输出一个既约分数。
【输入输出样例 1】
Input
4
1
3
1
2
2 1
4 2
4
【输入输出样例 2】
Input
3
1
1
2
1
3
2 10
2 5
3 8
3
【输入输出样例 3】
Input
3
1
2
1
2
2 2
3 4
3
Output
IMPOSSIBLE
Output
5/4
Output
2

 

因为只要的到最大值与最小值的比值,能有一条路径连通s,t即可,如果确定了最小值,只需要找到最小的最大值的边使得s,t连通,于是把边从小到大排序,从一到n枚举每一条边,设置他为最小边,在他后面按顺序加边,直到s与t连通,得到一个比值,不断更新答案即可;连通只要用并查集就行。

 

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<iomanip>
 4 #include<cstring>
 5 #include<cstdlib>
 6 #include<cstdio>
 7 #include<queue>
 8 #include<ctime>
 9 #include<cmath>
10 #include<stack>
11 #include<map>
12 #include<set>
13 #define inf 199999999
14 #define db double
15 using namespace std;
16 const int N=510,M=5010;
17 struct E{
18     int to,net,w;
19     int fr;
20 }e[M*2];
21 int n,m,num_e,head[N],s,t;
22 int fa[N];
23 void add(int x,int y,int w) {
24     e[++num_e].to=y;e[num_e].net=head[x];e[num_e].w=w;head[x]=num_e;e[num_e].fr=x;
25 }
26 int gcd(int a,int b) {
27     return a%b==0? b :gcd(b,a%b);
28 }
29 int comp(const E &a,const E &b ){
30     return a.w<b.w;
31 }
32 void init() {
33     for(int i=1;i<=n;i++) fa[i]=i;
34 }
35 int find(int x) {
36     if(x!=fa[x]) fa[x]=find(fa[x]);
37     return fa[x];
38 }
39 void Union(int x,int y) {
40     fa[y]=x;
41 }
42 int main() {
43     freopen("comf.in","r",stdin);
44     freopen("comf.out","w",stdout);
45     memset(head,-1,sizeof(head));
46     cin>>n>>m;int i;
47     int a,b;
48     a=inf;b=1;
49     for(i=1;i<=m;i++) {
50         int x,y,w;scanf("%d%d%d",&x,&y,&w);
51         add(x,y,w);
52     }
53     cin>>s>>t;
54     sort(e+1,e+m+1,comp);
55     for(i=1;i<=m;i++) {
56         init();int j;
57         int minn=e[i].w;
58         for(j=i;j<=m;j++){
59         int r1=find(e[j].fr),r2=find(e[j].to);
60         if(r1!=r2) {
61             Union(r1,r2);
62           
63         }
64         //        printf("%d %d\n",r1,r2);
65         if(find(s)==find(t)) break;
66         }
67         if(j>m) break;
68         if((db)e[j].w/(db)minn<(db)a/(db)b) a=e[j].w,b=minn;
69     }
70     int c=gcd(a,b);
71     a/=c,b/=c;
72     if(a==inf) {
73         puts("IMPOSSIBLE");return 0;
74     } 
75     if(b==1) printf("%d",a);
76     else printf("%d/%d",a,b);
77     return 0;
78 }
View Code

 


【数据范围】
0<N<=500;0<M<=5000;
- 4 -4. 奶牛跑步
【问题描述】
Bessie 准备用从牛棚跑到池塘的方法来锻炼. 但是因为她懒,她只准备沿着下坡的路跑
到池塘,然后走回牛棚.
Bessie 也不想跑得太远,所以她想走最短的路经. 农场上一共有 M(1<=M<=10,000)条路,
每条路连接两个用 1..N(1<=N<=1000)标号的地点. 更方便的是,如果 X>Y,则地点 X 的高度大
于地点 Y 的高度. 地点 N 是 Bessie 的牛棚;地点 1 是池塘.
很快, Bessie 厌倦了一直走同一条路.所以她想走不同的路,更明确地讲,她想找出
K(1<=K<=100)条不同的路经.为了避免过度劳累,她想使这 K 条路径为最短的 K 条路径.
请帮助 Bessie 找出这 K 条最短路经的长度.你的程序需要读入农场的地图, 一些从 Xi
到 Yi 的路径和它们的长度(Xi,Yi,Di).
所有(Xi,Yi,Di) 满足( 1<=Yi<Xi; Yi<Xi<=N, 1<=Di<=1,000,000 ).
【输入】
第 1 行: 3 个数: N,M,K
第 2..M+1 行: 第 i+1 行包含 3 个数 Xi,Yi,Di, 表示一条下坡的路.
【输出】
第 1..K 行: 第 i 行包含第 i 最短路径的长度,或−1 如果这样的路径不存在.如果多条路径
有同样的长度,请注意将这些长度逐一列出.
【输入输出样例】
Input
5
5
5
5
5
4
3
3
2
8
4
3
2
1
3
1
2
1
7
1
1
1
1
4
1
1
1
Output
1
2
2
3
6
7
-1
【样例解释】
路径分别为(5−1),(5−3−1),(5−2−1),(5−3−2−1),(5−4−3−1),(5−4−3−2−1)

 

k短路,A*搜所,构造反图,处理处终点到每个点的最短距离,每次开启队列里dist+dis[u]最小的来扩展,一旦扩展到终点,就意味着找到了一条最短路,当第K次扩展到的时候就是第K短路。。。

 

 1 #define ll long long
 2 #include<algorithm>
 3 #include<iostream>
 4 #include<iomanip>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cstdio>
 8 #include<queue>
 9 #include<ctime>
10 #include<cmath>
11 #include<stack>
12 #include<map>
13 #include<set>
14 using namespace std;
15 const int N=1010,M=10010;
16 struct E{
17     int to,net,w;
18 }e[M],e2[M];
19 int n,m,k,num_e,num;
20 int head[N],h[N];
21 void add(int x,int y,int w) {
22     e[++num_e].to=y;e[num_e].net=head[x];e[num_e].w=w;head[x]=num_e;
23 }
24 void add2(int x,int y,int w) {
25     e2[++num].to=y;e2[num].w=w;e2[num].net=h[x];h[x]=num;
26 }
27 int dis[N],cnt[N];
28 bool inq[N];
29 void spfa(int s,int t) {
30     queue<int> q;
31     q.push(s);
32     inq[s]=1;
33     memset(dis,0x3f,sizeof(dis));
34     dis[s]=0;
35     while(!q.empty()) {
36     int u=q.front();q.pop();
37     inq[u]=0;//不加会萎
38     for(int i=h[u];i;i=e2[i].net) {
39         int to=e2[i].to;
40         if(dis[to]>dis[u]+e2[i].w) {
41         dis[to]=dis[u]+e2[i].w;
42         if(!inq[to]) q.push(to),inq[to]=1;
43         }
44     }
45     }
46 }
47 struct Node{
48     int id,dist;
49     bool operator<(const Node & b)const{
50     return dist + dis[id] > b.dist +dis[b.id];
51     }
52 };//Yinpengzhe200104234532
53 void a_star(int s,int t) {
54     priority_queue<Node> q;
55     Node tmp={s,0},tt;
56     q.push(tmp);
57     while(!q.empty()) {
58     tmp=q.top();q.pop();
59     cnt[tmp.id]++;
60     if(tmp.id==t) {
61         printf("%d\n",tmp.dist);
62         if(cnt[tmp.id]==k) return;
63     }
64     if(cnt[tmp.id]>k) continue;
65     for(int i=head[tmp.id];i;i=e[i].net) {
66         int to=e[i].to;
67         tt=(Node){to,tmp.dist+e[i].w};
68         q.push(tt);
69     }
70     }
71 }
72 int main() {
73     freopen("cowjog.in","r",stdin);
74     freopen("cowjog.out","w",stdout);
75     cin>>n>>m>>k;int i;
76     for(i=1;i<=m;i++) {
77         int x,y,w;
78         scanf("%d%d%d",&x,&y,&w);
79         add(x,y,w);add2(y,x,w);
80     }
81     spfa(1,n);
82     a_star(n,1);
83     while(cnt[1]<k) puts("-1"),cnt[1]++;
84     return 0;
85 }
View Code

。。。。。。

 

转载于:https://www.cnblogs.com/ypz999/p/flow.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值