[SinGuaRiTy] 2017-07-24 NOIP2015 模拟赛
【SinGuLaRiTy-1030】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.
对于所有题目: Time Limit: 1s | Memory Limit: 256MB
旅行 (travel)
题目描述
Mr_H 旗下的 n 个OIer 坐船外出旅行!
但是他们只有一艘船,虽然船能装下全部的Oier,但太拥挤将会影响众OIer 的心情,所以Mr_H决定选择一部分Oier 去。我们假设,每个人单独坐船的快乐程度是Ci,而船上每多一个人,他的快乐程度会减去Di。
现在你的任务是帮助Mr_H 计算,选择那些人,才能使船上所有人的快乐程度之和达到最大。
输入
第 1 行是一个整数n,表示OIer 的人数;
第 2 行有n 个整数,第i 个整数表示第i 个人人单独坐船的快乐程度Ci(1<=Ci<=10000);
第 3 行有n 个整数,第i 个整数表示每多1 人,第i 个人快乐程度的下降值Di(1<=Di<=10)。
输出
第 1 行一个整数,是最大的快乐程度之和;
第 2 行一个整数,是最大的快乐程度之和所对应的汽艇上的人数(若有多种方案,则输出人数最多的)。
样例数据
样例输入 | 样例输出 |
6 |
18 |
<数据范围>
对于30%的数据,n<=20;
对于100%的数据,n<=1000。
解析
很明显,直接枚举是没有必要的。我们可以枚举人数 K,然后算出在这种情况下每个人的快乐程度。然后贪心地取前K 大的数,这样就得到了 O(n^2*logn) 的算法。
Code
#include<iostream> #include<cstdlib> #include<cstdio> #include<algorithm> #include<cmath> #define MAXN 1010 #define INF 2000000000 using namespace std; int n,d[MAXN][MAXN]; int ans=-INF; void init() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&d[1][i]); for(int i=1;i<=n;i++) { int x; scanf("%d",&x); for(int j=2;j<=n;j++) d[j][i]=d[j-1][i]-x; } } int main() { init(); int s; for(int i=1;i<=n;i++) { sort(d[i]+1,d[i]+1+n); if(d[i][n-i+1]<=0) break; int x=0; for(int j=0;j<i;j++) x+=d[i][n-j]; if(ans<=x) { ans=x; s=i; } } printf("%d\n%d",ans,s); return 0; }
数据 (data)
题目描述
Mr_H 出了一道信息学竞赛题,就是给 n 个数排序。输入格式是这样的:
试题有若干组数据。每组数据的第一个是一个整数 n,表示总共有 n 个数待排序;接下来 n 个整数,分别表示这n 个待排序的数。
例如:3 4 2 –1 4 1 2 3 4,就表示有两组数据。第一组有3 个数(4,2,-1),第二组有4个数(1,2,3,4)。可是现在Mr_H 做的输入数据出了一些问题。例如:2 1 9 3 2 按理说第一组数据有2 个数(1,9),第二组数据有3 个数,可是“3”后面并没有出现三个数,只出现了一个数“2”而已!
现在 Mr_H 需要对数据进行修改,改动中“一步”的含义是对文件中的某一个数+1 或-1,写一个程序,计算最少需要多少步才能将数据改得合法。
输入
第一行一个整数m,表示Mr_H 做的输入数据包含的整数个数。第二行包含m 个整数a[i],每个整数的绝对值不超过10000。
输出
一个整数,表示把数据修改为合法的情况下,最少需要多少步。
样例数据
样例输入1 | 样例输出1 |
4 |
2 |
样例输入2 | 样例输出2 |
10 |
3 |
<数据范围>
对于 20%的数据,m<=10, |a[i]|<=5;
对于60%的数据,m<=5000, |a[i]|<=10000
对于100%的数据,m<=100000, |a[i]|<=10000
解析
先来谈谈图论的解法,简单的说就是连边然后找最短路:若a[i]>0,那么就说明这个数可以用来表示数列的长度,我们就从a[i]连一条边到下一个数列i+a[i]+1,并设定其权值为0——因为这里实际上不需要耗费任何操作。如果i+a[i]+1超过了n怎么办呢?我们就在任意两个相邻的数间连一条权值为1的边——这里的左移右移就相当于加和减的操作,是需要消耗布数的。注意:第一个点和第二个点不能连,会出毒的。另外,若a[1]<0,我们显然要将其变为正数,于是这样的话我们需要在ans后面直接加上a[1],即使a[1]变为正数的步数。至于最后的答案嘛,直接跑1~n的最短路就行了。
不过,这么玄(zhèng) 学(cháng) 的东西怎么能是标准解法呢?要看DP+优化的同学还是直接 [下载PDF题解] 好了。
Code
#include<cstdio> #include<queue> #include<vector> #include<iostream> #define need 110003 #define inf 1e9 using namespace std; int n,nn; inline void Read(int &d) { char t=getchar();bool mark=false; while(t<'0'||t>'9') {if(t=='-') mark=true;t=getchar();} for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0'; if(mark) d=-d; } struct bian { int la,en,len; }; vector<bian> w; int tot,fi[need]; void add(const int &a,const int &b,const int &c) { tot++; w.push_back((bian){fi[a],b,c}); fi[a]=tot; } int s,e; struct fy { int dis,id; bool operator< (const fy &b) const { return dis>b.dis; } }; priority_queue<fy> q; bool getans[need]; int dis[need]; void dijkstra(int s) { for(int i=1;i<=nn;i++) { dis[i]=inf; getans[i]=false; } q.push((fy){0,s}); dis[s]=0; int x,y,t; while(!q.empty()) { while(!q.empty()&&getans[q.top().id]) q.pop(); x=q.top().id; if(x==e) return ; getans[x]=true; q.pop(); for(t=fi[x];t;t=w[t].la) { y=w[t].en; if(getans[y]) continue; if(dis[y]>dis[x]+w[t].len) { dis[y]=dis[x]+w[t].len; q.push((fy){dis[y],y}); } } } } int main() { w.resize(1); int n; scanf("%d",&n); int dans=0; int a; Read(a); if(a<0) dans=-a,add(1,2,0); else add(1,a+2,0); nn=max(nn,a+2); for(int i=2,a;i<=n;i++) { Read(a); if(a>=0) add(i,i+a+1,0); nn=max(nn,i+a+1); add(i,i+1,1); add(i+1,i,1); } for(int i=n+1;i<=nn;i++) { add(i,i+1,1); add(i+1,i,1); } s=1; e=n+1; dijkstra(s); cout<<dis[e]+dans; return 0; }
<DP+优化 Code>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream>//乱搞 #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int Max = 100000; const int INF = 0x3f3f3f3f; struct node{ int val, pos; node(){} node(int a, int b){ val = a, pos = b;} bool operator < (const node & X) const{ return val > X.val; } }; int N; int A[Max + 5], Dp[Max + 5]; priority_queue<node>Q; bool getint(int & num){ char c; int flg = 1; num = 0; while((c = getchar()) < '0' || c > '9'){ if(c == '-') flg = -1; if(c == -1) return 0; } while(c >= '0' && c <= '9' ){ num = num * 10 + c - 48; if((c = getchar()) == -1) return 0; } num *= flg; return 1; } int main(){ getint(N); for(int i = 1; i <= N; ++ i) getint(A[i - 1]), A[i - 1] += i; Q.push(node(A[0], A[0])); memset(Dp, 0x3f, sizeof Dp ); int minnum = INF; for(int i = 1; i <= N; ++ i){ while(! Q.empty() && Q.top().pos < i){ minnum = min(minnum, Q.top().val - 2 * Q.top().pos); Q.pop(); } Dp[i] = min(Dp[i], minnum + i); if(! Q.empty()) Dp[i] = min(Dp[i], Q.top().val - i); Q.push(node(Dp[i] + A[i], A[i])); } printf("%d\n", Dp[N]); return 0; }
业务 (business)
题目描述
Mr_H 谋得一份兼职——货车司机,从此以后他将会开着货车穿行在C 国的各大城市之间。
C 国中有n 座城市(编号为1~n),并且有m 条双向公路,每条公路连接两座不同的城市。货车从任意一座城市出发都可以抵达任意另一座城市。在每条公路上,都有一个收费站,通过的车辆需要交纳一定过路费。可能有多条公路连接相同的两座城市。
为了增加财政收入,C 国还在每座城市也设置了收费站。并且规定,车辆从一座城市到另一座城市的费用是,所经过公路费用和,加上所经过的城市中费用的次大值(这里的次大可以和最大相同,但是城市不同)。
现在Mr_H 告诉你今年k 次业务运送货物的起点、终点城市列表,请你帮忙计算,每次业务需要交纳的最低过路费。
输入
第一行包含三个用一个空格隔开的整数:n,m,k。其意义如题目描述。
第 2 到第 n+1 行:第i+1 行包含一个单独的整数 c(1<=c<=100000),表示城市i 的费用。
接下来的m 行,每行包含三个整数a,b,w,表示一条公路连接城市a 和城市b(1<=a,b<=n),其过路费为w(1<=w<=100000)。
最后的 k 行,每行包含两个整数:s,t,表示一次业务的起点和终点(1<=s,t<=n 且 s!=t)。
输出
共k 行,每行一个整数,表示从城市s 到t 的最少过路费。
样例数据
样例输入 | 样例输出 |
5 7 3 |
4 |
<样例解释>
包含 5 个城市的样例图形如下:
●城市1 到城市3 的道路的“边过路费”为 2,“点过路费”为 2(城市2 的费用为次大)。所以总的花费为 2+2=4 。
●要从城市1 走到城市4 ,可以从城市1 走到城市3,再走到城市5,最后到达城市4 。如果这么走的话,需要的“边过路费”为 2+1+1=4,需要的点过路费为 3(城市3 或城市4 的点过路费次大),所以总的花费为 4+3=7。
●从城市2 走到城市3 的最佳路径是从城市2 出发,抵达城市5,最后到达城市3,这么走的话,边过路费为 3+1=4,点过路费为4,总花费为 4+4=8。
<数据范围>
对于 20%的数据,n<=10, m<=20
对于50%的数据,n<=100, m<=5000
对于100%的数据,1<=n<=250 , 1<=m<=10000 , 1<=k<=10000 ,其中有50%的数据点权没有重复。
解析
(考试的时候用的DFS,没D出来)
令:ans[x][y]=从x 到y 的最小过路费!计算ans[x][y]可以用DIJKstra 算法或SPFA 算法!
令:dist[i][0]=从k 出发到i 点经过的点费用最大值为a[k]的情况的最小边费用和;dist[i][1]=从k 出发到i 点经过的点费用次大值为a[k]的情况的最小边费用和
于是有: ans[x][y]=min{dist[x][0]+dist[y][1],dist[x][1]+dist[y][0]}+a[k]}其中 1<=k<=n
计算出ans[x][y]后,回答询问就很简单了。
时间复杂度 O(n2logm+k) (DIJKstra 堆优化后的时间复杂度)
Code
#include<cstdio> #include<cmath> #include<algorithm> #include<iostream> #include<cstring> #include<queue> #define MAXN 250 #define MAXM #define DIST(x)(tmp[x.pos][x.hm]) #define VIS(x)(vis[x.pos][x.hm]) #define INF 0x3f3f3f3f using namespace std; typedef long long int LL; int getint() { int rn=0; char c=getchar(); while(c<'0'||'9'<c) c=getchar(); while('0'<=c&&c<='9') { rn=rn*10+c-'0'; c=getchar(); } return rn; } int G[MAXN+10][MAXN+10]; int Val[MAXN+10]; int dist[MAXN+10][MAXN+10]; int N,M,K; int tmp[MAXN+10][2]; bool vis[MAXN+10][2]; struct point { int pos,w; bool hm; point(){} point(int a,bool b,int c){pos=a,hm=b,w=c;} bool operator < (const point &a)const { return w>a.w; } }; priority_queue<point>que; void insert(point newed) { if(DIST(newed)<newed.w) return; DIST(newed)=newed.w; que.push(newed); } void Dijkstra(int sc) { memset(tmp,0x3f,sizeof(tmp)); memset(vis,0,sizeof(vis)); insert(point(sc,0,0)); point now,newed; while(!que.empty()) { now=que.top(); que.pop(); if(VIS(now)) continue; else VIS(now)=1; for(int v=1;v<=N;++v) if(G[now.pos][v]) { newed.pos=v; newed.hm=now.hm; newed.w=DIST(now)+G[now.pos][v]; if(Val[v]>Val[sc]) { if(now.hm) continue; else { newed.hm=1; insert(newed); } } else if(Val[v]==Val[sc]) { if(v==sc) insert(newed); else { insert(newed); newed.hm=1; insert(newed); } } else insert(newed); } } } int main() { scanf("%d%d%d",&N,&M,&K); int i,j; for(i=1;i<=N;++i) scanf("%d",&Val[i]); int a,b,c; for(i=1;i<=M;++i) { scanf("%d%d%d",&a,&b,&c); G[a][b]=(!G[a][b]||G[a][b]>c?c:G[a][b]); G[b][a]=(!G[b][a]||G[b][a]>c?c:G[b][a]); } memset(dist,0x3f,sizeof(dist)); for(int sc=1;sc<=N;++sc) { Dijkstra(sc); for(i=1;i<=N;++i) for(j=1;j<=N;++j) dist[i][j]=min(dist[i][j],min(tmp[i][0]+tmp[j][1],tmp[i][1]+tmp[j][0])+Val[sc]); } for(i=1;i<=K;++i) { scanf("%d%d",&a,&b); printf("%d\n",dist[a][b]); } return 0; }
Time: 2017-07-24