• 20181030_队测


    T1

    题目描述

    Bessie看到了一个山脉,它想知道最宽的山有多宽,山脉可以抽象成N(N<=100000)个数表示高度,山就定义成一段数,上半段不降,下半段不增比如1222345543221这样的。。。山的宽度就是这段数的个数。。。

    输入输出格式

    输入格式

    第一行,N
    接下来 N行,每行一个数表示山脉。。

    输出格式

    一行,最宽的山脉宽度。。

    样例

    INPUT

    7
    3
    2
    3
    5
    4
    1
    6

    OUTPUT

    5

    HINT

    用 longint(pascal)或int(c++)就可以了。。

    SOLUTION

    纯模拟

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    using namespace std;
    #define Max(a,b) ((a>b)?a:b)
    #define Min(a,b) ((a<b)?a:b)
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    	while (ch>='0'&&ch<='9') {x=x*10+ch-48;ch=getchar();}
    	return x*f;}
    const int N=101000;
    int n,a[N],L,R,lstL,lstR,ans=1;
    int main(){
    //	freopen("mount.in","r",stdin);
    //	freopen("mount.out","w",stdout);
    	int i,j;
    	n=read();a[1]=read();L=1;R=1;lstR=1;int now=1,flg=0;
    	for (i=2;i<=n;++i){
    		a[i]=read();
    		if ((a[i]<a[i-1])||(a[i]>a[i-1])) flg=1;
    		if ((a[i]<a[i-1])&&(!now)) {lstR=i;}//呈下坡并允许下坡 
    		if ((a[i]<a[i-1])&&(now)) {now=0;lstR=i;}
    		if ((a[i]>a[i-1])&&(!now)) {ans=Max(ans,i-L);L=lstR;now=1;flg=0;}
    	}
    	ans=Max(ans,n-L+1);
    	if (!flg) {ans=Max(ans,n-lstR+1);}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    T2

    题目描述

    You are appointed director of a famous concert hall, to save it from bankruptcy. The hall is very popular, and receives many requests to use its two fine rooms, but unfortunately the previous director was not very efficient, and it has been losing money for many years. The two rooms are of the same size and arrangement. Therefore, each applicant wishing to hold a concert asks for a room without specifying which. Each room can be used for only one concert per day.
    In order to make more money, you have decided to abandon the previous fixed price policy, and rather let applicants specify the price they are ready to pay. Each application shall specify a period [i, j] and an asking price w, where i and j are respectively the first and last days of the period (1 <= i <= j <= 365), and w is a positive integer in yen, indicating the amount the applicant is willing to pay to use a room for the whole period.
    You have received applications for the next year, and you should now choose the applications you will accept. Each application should be either accepted for its whole period or completely rejected. Each concert should use the same room during the whole applied period.
    Considering the dire economic situation of the concert hall, artistic quality is to be ignored, and you should just try to maximize the total income for the whole year by accepting the most profitable applications.
    大意:
    有二个旅馆,出租给别人。一个旅馆在某一个时段只能租给一个人使用。对方给出要租用的时间及出的价格。问如何安排,才能挣到最多的钱。

    输入输出格式

    输入格式

    The input has multiple data sets, each starting with a line consisting of a single integer n(N<=1000), the number of applications in the data set. Then, it is followed by n lines, each of which represents one application with a period [i, j] and an asking price w yen in the following format.
    i j w
    A line containing a single zero indicates the end of the input.
    The maximum number of applications in a data set is one thousand, and the maximum asking price is one million yen.

    输出格式

    For each data set, print a single line containing an integer, the maximum total income in yen for the data set.

    样例

    INPUT

    4
    1 2 10 //从第一天到第二天,付出代价为10元
    2 3 10 //从第二天到第三天,付出代价为10元
    3 3 10
    1 3 10
    6
    1 20 1000
    3 25 10000
    5 15 5000
    22 300 5500
    10 295 9000
    7 7 6000
    8
    32 251 2261
    123 281 1339
    211 235 5641
    162 217 7273
    22 139 7851
    194 198 9190
    119 274 878
    122 173 8640
    0

    OUTPUT

    30
    25500
    38595

    HINT

    样例挺好推的,自己手推一下吧

    SOLUTION

    dp

    这是一道类似于背包问题的dp题。

    首先很明显的是,当我们只有一个旅馆时,就是一个裸背包对吧。

    [dp[i]=Max(dp[i-1],dp[i-book[j].start]+book[j].value) ]

    其中,(dp[i])代表的是第(i)时刻可以获得的最高价值。这个方程很好理解。

    当我们手头上有两个旅馆时,我们就设(dp)数组为(dp[i][j])表示两个旅馆分别在第(i)时刻与第(j)时刻能够获得的最大价值。

    首先,根据题意,(dp[i][j])(dp[j][i])从本质上来讲就属于同一种情况。所以,为了方便我们这里不妨设(ileq j)

    显然地,只要我们的第(i,j)时刻打算闲置的话,(dp[i][j])就可以从(dp[i-1][j],dp[i][j-1])转移来。

    仿照第一个式子,当时刻(i,j)分别为订单(a,b)的终点时刻时,我们可以得到一个二维的转移方程形如:

    [dp[i][j]=Max(dp[book[a].start-1][book[b].start-1]+book[a].value+book[b].value) ]

    不过,单单如此是不够的。

    (i≠j)时,上面的式子一定成立,当(i=j)时,就要加上特判:(a≠b)。(咋肥四啊两个旅馆抢一个订单)

    到这里应该没有什么问题了吧。

    然后你就T掉了。

    没错还有一件事:时间复杂度。

    对于我们如此暴力的枚举方式,1s肯定是跑不完的((O(n^4))的时间复杂度qwq)。
    怎么优化?
    回过头来想想我们的第二个方程,

    [dp[i][j]=Max(dp[book[a].start-1][book[b].start-1]+book[a].value+book[b].value) ]

    [dp[book[a].start-1][book[b].start-1]--->dp[i][j] ]

    是不是可以等价于

    [dp[book[a].start-1][book[b].start-1]--->dp[i][book[b].start-1]--->dp[i][j] ]

    可以看出我们一次转移不一定要双线同时推进,而这里又可以引用前面的思想:(dp[i][j])(dp[j][i])从本质上来讲就属于同一种情况”,也就是说,我们这里也不必拘泥于到底是(i)(j),而是从整体上看,就可以看出,双线同时推进问题可以轻松转化成为单线推进问题。

    所以我们可以得到一个新的式子:$$dp[i][j]=Max(dp[i][book[a].start-1]+book[a].value)$$

    大致思路已经成形,不过这么做的话就又有一个小问题:当(i=j)时,单线转移不可避免地会出现重复(一个订单被同时占用),这怎么办呢,如果再要写特判可能会非常复杂,所以(O(n^4))的方法这时候就能够派上用场了。当(i=j)时,我们考虑枚举(a,b)来解决。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    using namespace std;
    #define Max(a,b) ((a>b)?a:b)
    #define Min(a,b) ((a<b)?a:b)
    const int N=400,M=1010,DAYS=365;
    int n,m,dp[N][N];
    struct BOOK{int stt,edd,w;}bk[M];
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    	while (ch>='0'&&ch<='9') {x=x*10+ch-48;ch=getchar();}
    	return x*f;}
    int main(){
    	int i,j;
    	while (1){
    		n=read();if (!n) return 0;
    		memset(dp,0,sizeof(dp));
    		for (i=1;i<=n;++i) {bk[i].stt=read();bk[i].edd=read();bk[i].w=read();}
    		for (i=1;i<=DAYS;++i){
    			for (j=1;j<=DAYS;++j){
    				if (i>j) {dp[i][j]=dp[j][i];continue;}
    				dp[i][j]=Max(dp[i-1][j],dp[i][j-1]);
    				if (i==j){
    					for (int a=1;a<=n;++a){
    						if (bk[a].edd!=i) continue;
    						for (int b=a+1;b<=n;++b){
    							//if (a==b) continue;
    							if (bk[b].edd!=j) continue;
    							dp[i][j]=Max(dp[i][j],dp[bk[a].stt-1][bk[b].stt-1]+bk[a].w+bk[b].w);
    						}
    					}
    				}
    				if (i<j){
    					for (int a=1;a<=n;++a){
    						if (bk[a].edd!=j) continue;
    						dp[i][j]=Max(dp[i][j],dp[i][bk[a].stt-1]+bk[a].w);
    					}
    				}
    			}
    		}
    		int ans=0;
    		for (i=1;i<=DAYS;++i)
    			for (j=1;j<=DAYS;++j)
    				ans=Max(ans,dp[i][j]);
    		//printf("%d
    ",dp[1][198]);
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    T3

    题目描述

    给定一个正整数N(N<=500),将其分成若干个正整数,求这若干个数最大的最小公倍数。答案保证小于10^25。

    输入输出格式

    输入格式

    4

    输出格式

    4

    样例

    INPUT

    7

    OUTPUT

    12

    HINT

    SOLUTION

    数学
    高精+dp

    一个数拆成多个数后(lcm)最大?

    多个数的(lcm)是什么啊?多个数的(lcm=frac {这些数的乘积}{这些数的gcd})
    所以这题不仅要使乘积最大,还要使(gcd)最小。

    互素情况下(gcd)是最小的,所以为了使这些数尽可能地互素呢,我们的答案优先使用质数以及质数的幂构造。

    30pts:dfs暴力

    500以内的质数很好求,所以,再求出所有质数之后,直接暴搜,枚举所有可能的质数的指数,取(max)作为答案。

    100pts dp

    其实对于一个质数(p),对于(n)(p)对答案的贡献形式可能会有(p^0,p^1,ldots ,p^k(p^kleq n))所以我们的(dp)数组(dp[i][j])(i)为当前质数的编号,(j)为当前的和)就如此记录答案:(dp[i][j]=Max(dp[i][j-p^k]))(其中(k)需要枚举)

    同时对于一个数(j),也可以表示成(j-1+1),所以答案也可以从前一个数转移来:(dp[i][j]=dp[i][j-1])

    (10^{25})是会爆long long的qwq,要高精
    完成。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    using namespace std;
    const int NUM=100,N=510;
    const int M=10000;
    struct Bignumber{
    	int len,b[NUM];
    	Bignumber(){len=0;memset(b,0,sizeof(b));}
    	inline void clear(){len=0;memset(b,0,sizeof(b));}
    	Bignumber(int x){memset(b,0,sizeof(b));len=0;if (x==0) {len=1;return;}
    		while (x) {b[++len]=x%M;x/=M;}}
    	inline void print(){
    		printf("%d",b[len]);
    		for (int i=len-1;i>=1;--i) printf("%04d",b[i]);puts("");}
    	inline Bignumber operator *(const int &a)const{
    		Bignumber c;c.clear();c.len=len+5;
    		for (int i=1;i<=len;++i){
    			c.b[i]+=b[i]*a;c.b[i+1]=c.b[i]/M;c.b[i]%=M;}
    		while (!c.b[c.len]&&c.len>1) c.len--;
    		return c;
    	}
    };
    inline Bignumber Max(Bignumber a,Bignumber b){
        if (a.len>b.len) return a;
        if (b.len>a.len) return b;
        for (int i=a.len;i>=1;--i){
            if (a.b[i]==b.b[i]) continue;
            if (a.b[i]>b.b[i]) return a;else return b;}
        return a;}
    int n,prm[N],used[N],tmp=0;
    Bignumber dp[N][N];
    inline void eprime(){
    	int i,j;
    	memset(used,0,sizeof(used));
    	for (i=2;i<=500;++i){
    		if (!used[i]) {used[i]=1;prm[++tmp]=i;//printf("%d ",i);
    			for (j=1;(i*j)<=500;++j) used[i*j]=1;}
    	}
    }
    inline int fast_p(int a,int b)
    	{int ans=1; while (b) {if (b&1) ans=ans*a;a=a*a;b=b>>1;}return ans;}
    int main(){
    	int i,j;
    	eprime();
    	while (scanf("%d",&n)!=EOF){
    		Bignumber ans;ans.clear();
    		for (i=0;i<=n;++i)
    			for (j=0;j<=tmp;++j)
    				{dp[j][i].clear();dp[j][i].b[1]=1;dp[j][i].len=1;}
    		for (i=1;i<=tmp;++i){
    			for (j=1;j<=n;++j){
    				dp[i][j]=dp[i-1][j];
    				for (int k=1;fast_p(prm[i],k)<=j;++k){
    					dp[i][j]=Max(dp[i][j],dp[i-1][j-fast_p(prm[i],k)]*(fast_p(prm[i],k)));
    					//printf("%d:%d(%d) %d ",j,i,prm[i],k);dp[i][j].print();
    				}
    			}
    		}
    		for (i=1;i<=tmp;++i) ans=Max(ans,dp[i][n]);ans.print();
    	}
    	return 0;
    }
    

    T4

    题目描述

    农夫约翰已经为一次赛跑选出了K(2 <= K <= 40)头牛组成了一支接力队。赛跑在农夫约翰所拥有的农场上进行,农场的编号为1到N(4 <= N < 800),N个农场之间共有M(1 <= M <= 4000)条双向道路,每条双向道路连接一对不同的农场,一对不同的农场之间最多只有一条双向道路。
    你将得到每条牛为了穿过每个道路所需要的时间。比赛的起点设在1号农场,终点设在N号农场,比赛开始后第一头牛开始从1号农场出发,在N号农场到达终点。第一头牛结束之后马上就让下一头牛从1号农场开始,跑向N号农场,直到所有的牛都跑过一次为止。
    作为比赛的规则,任意的2头牛跑过的路线不能完全相同(路线是指跑过的农场的序列)。但对某头牛来说它可以经过某个农场多次。
    写一个程序为农夫约翰的接力队计算所有的牛完成这次比赛所需的最少的时间。可以保证对所有的测试数据要求的最少时间是一定存在的。

    输入输出格式

    输入格式

    第1行包含3个整数:K, N, M
    从第2行到第M+1行,每行包括描述一条道路的3个整数,分别表示该道路连接的两个农场的编号,以及每条牛穿过这条路所需的时间(范围在1 .. 9500之间)。

    输出格式

    输出文件仅一行,表示这K头奶牛完成接力赛所用的最短时间(假定每单位长度需要花费一单位时间)

    样例

    INPUT

    4 5 8
    1 2 1
    1 3 2
    1 4 2
    2 3 2
    2 5 3
    3 4 3
    3 5 4
    4 5 6

    OUTPUT

    23

    HINT

    样例说明
    奶牛1: 1->2->5 4
    奶牛2: 1->3->5 6
    奶牛3: 1->2->1->2->5  6
    奶牛4: 1->2->3->5 7

    SOLUTION

    贪心思维题

    ※[Attention1]:样例中的k短路出现跑重边的情况
    ※[Attention2]:但是和(n)号点连接的边并不适用。

    (我知道可能只有我一个人并没有意识到[Attention2]但是我仍然认为题面没有任何一个字去强调这一点是有问题的。因为它的样例就是摆明了你可以走重边而你又不说什么到了终点就停止了这种话你跟我说理解题意那我问你你见过有哪一场比赛还跑重边的啊真是奇了怪有跑重边的选手就不能有从终点跑出来的选手咯)

    和现在的毒瘤数据结构不一样,本题很有老一点的题目的感觉,比起暴力数据结构或者是****高级算法,本题更加注重考查的是思维。

    当然一开始读题就很可能会往k短路上想,但是我们求的是什么?前k短路的和。
    再况且题目有冒出来一个什么跑重边的高级设定,一般的k短路算法并不能适用本题。(而且我也不会qwq)

    其实这题就是利用了djikstra的思想进行实现。

    既然不能从终点跑出来,那么到最后(n)的访问次数一定为(k),其他点的访问次数不一定为(k),不过当前这个点对k短路的贡献情况只有k种(至于为什么是k种我们后面再说)于是不同于普通dijkstra的(dist[i]),对于第(i)个点在(k)短路上与源点距离,我们有(dist[i][k])

    我们要做的就是像普通的djikstra一样松弛,但是不用限制同一个点的访问次数,因为我们要求的是每个点在第1~k短路上的情况,还是因为每个点可能会被访问多次,访问所带来的更新值很有可能作为某条某短路答案的贡献值,我们必须保留下来到跑某短路时再用。所以可能有超过k个值想要加入到我们的(dist)数组上,要取最优,很明显(dist[i])(0)(k-1)有单调性,所以考虑二分查找插入,多于(k-1)的地方舍去(虽然我代码里好像保留的是(0)(k)的,但是无伤大雅嘛),舍去部分像栈一样直接推出,我们就选择用(top[i])维护第(i)点的栈顶编号。

    用不同(k)(dist[i][k])去做新的松弛操作时,考虑用(btm[i])来维护当前松弛操作引用的值的位置。

    代码在下面,不长。这题思维难度比代码实现难度高很多。要不是那个神奇的重边和终点的设定我一定会大呼是道好题。

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    typedef long long LL;
    #define Max(a,b) ((a>b)?a:b)
    #define Min(a,b) ((a<b)?a:b)
    const int N=810,KK=50,M=4010;
    const int INF=1061109567;
    int n,m,K,sch,dist[N][KK],cnt=0;
    short used[N],btm[N],top[N],head[N];
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    	while (ch>='0'&&ch<='9') {x=x*10+ch-48;ch=getchar();}
    	return x*f;}
    struct EDGE{int nxt,v,w;}e[2*M];
    inline void add(int u,int v,int w) {e[++cnt].nxt=head[u];e[cnt].v=v;e[cnt].w=w;head[u]=cnt;}
    inline void bsh(int l,int r,int x,int fdd){
    	if (l==r) {sch=l;return;}
    	if (l+1==r) {if (dist[x][l]>=fdd) sch=l;else sch=r;return;}
    	int mid=(l+r)>>1;
    	if (dist[x][mid]>=fdd) bsh(l,mid,x,fdd);else bsh(mid+1,r,x,fdd);}
    inline void update(int x,int w){
    	int i,j;
    	bsh(btm[x],top[x],x,w);//printf("%d:%d %d
    ",x,sch,w);
    	for (i=Min(top[x],K)+1;i>sch;--i) dist[x][i]=dist[x][i-1];
    	dist[x][sch]=w;top[x]=Min(top[x]+1,K);}
    void dij_beta(){
    	int i,j;memset(used,0,sizeof(used));
    	int u=1,vis=0;
    	while (1){
    		int rec=INF,flg=1;
    		used[u]++;
    	/*	printf("nowu:%d bottomu:%d
    ",u,btm[u]);
    		for (i=1;i<=n;++i) {printf("%d:bottom:%d top:%d dist:",i,btm[i],top[i]);
    			for (j=0;j<K;++j) printf("%d ",dist[i][j]);puts("");}
    	*/
    	//	if (dist[u][btm[u]]>=INF) return;
    		for (i=head[u];i;i=e[i].nxt){
    			int v=e[i].v,w=e[i].w;
    			if (used[v]>=K) continue;flg=0;
    			update(v,dist[u][btm[u]]+w);}
    		if (flg) vis++;
    		btm[u]++;
    	//	printf("%d
    ",dist[2][1]);
    		for (i=1;i<n;++i)
    			{if (rec>dist[i][btm[i]]) {rec=dist[i][btm[i]];u=i;}}
    		if (top[n]>=K&&dist[u][btm[u]]>=dist[n][K]) return;
    	}
    }
    int main(){
    	int i,j;
    	K=read();n=read();m=read();memset(head,0,sizeof(head));
    	for (i=1;i<=m;++i) {int u=read(),v=read(),w=read();add(u,v,w);add(v,u,w);}
    	memset(dist,0x3f,sizeof(dist));
    	memset(btm,0,sizeof(btm));
    	memset(top,0,sizeof(top));
    	//for (i=1;i<=n;++i) top[i]=1,btm[i]=0;
    	//for (i=0;i<=K;++i) dist[1][i]=0;
    	top[1]=1;dist[1][0]=0;
    	dij_beta();
    	LL ans=0;
    	for (i=0;i<K;++i) {ans+=dist[n][i];/*printf("dist[n][%d]:%d
    ",i,dist[n][i]);*/}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    数据库
    linux
    linux
    python_函数进阶1
    python_函数初识
    python_文件操作
    python_基础数据类型补充
    python 小数据池,代码块总览
    python_基础数据类型二
    python_基础数据类型一
  • 原文地址:https://www.cnblogs.com/hkpls/p/9889572.html
Copyright © 2020-2023  润新知