2018 wannafly winter camp day1 总结

A 机器人

这个题分成两大种情况。

第一种情况: 要经过的仓库只在A 区。

先找到编号最小和编号最大的仓库

  • 如果只要经过一个仓库,而且这个仓库就是起点   输出  0.
  • 如果起点在编号最小的仓库。 答案就是 (编号最大的转折点 - 起点编号)* 2;
  • 如果起点在编号最大的仓库。 答案就是 (起点编号 - 编号最小的转折点)* 2;
  • 如果起点在中间,  答案就是 (编号最大的转折点  - 编号最小的转折点) *  2;

第二种情况: 要经过的仓库 A 区 B 区都有

  • 如果起点在编号最小的仓库, 如果在 起点 和 B 区编号最小的仓库之间有转折点,答案就是 (编号最大的转折点 - 起点编号 + 费用) * 2, 反之答案就是  (编号最大的转折点  - 编号最小的转折点 +  费用) *  2;
  • 如果起点在编号最大的仓库,同上。
  • 如果起点在中间。(编号最大的转折点  - 编号最小的转折点 +  费用) *  2;
#include<bits/stdc++.h>
using namespace std; 
const int N = 1e5+1000;
int n,m,r,k,s;
int maxa,maxb,mina,minb,f[200],ans,xx,yy;
int main(){	
	int x,y;
	scanf("%d%d%d%d%d",&n,&r,&m,&k,&s);
	maxb = 0;
	minb = n+1;
	maxa = s; // 这个地方有没有考虑,起点肯定是必须经过的点啊。
	mina = s;
	for (int i = 0; i < r; ++i){
		scanf("%d%d",&x,&y);
		maxa = max(maxa,x);   //最小编号的仓库,和最大编号的仓库。
		mina = min(mina,x);

		if (y == 1){
			maxb = max(maxb,x);  // 找的是 b 区中,编号最小和最大的仓库。
			minb = min(minb,x);
		}
	}

	for (int i = 1; i <= m; ++i)
		scanf("%d",&f[i]);
	f[0] = 1; f[++m] = n;
	xx = 1; yy = n;
	for (int i = 0; i <= m; ++i){
		if (f[i] <= mina) xx = max(xx,f[i]);  //找可以包含所有仓库的最小和最大转折点。
		if (f[i] >= maxa) yy = min(yy,f[i]);
	}


	if (maxb == 0){  //不需要经过b区的仓库。
		if (mina == maxa){ // 重写之后,这个地方没有考虑。有可能只要经过一个点。还只是起点。

		} else
		if (maxa == s){ // 起点是编号最大的仓库。
			ans = 2*(s - xx);
		} else
		if (mina == s){  //起点是编号最小的仓库。
			ans = 2 * (yy - s);
		} else{  //起点在中间。
			ans = (yy - xx) * 2;
		}

	} else{  //经过B 区的仓库。
		if (maxa == s){ //类似。
			for (int i = 0; i <= m; ++i){
				if (f[i] <= s && f[i] >= maxb){
					ans = 2 * (s - xx + k);
					printf("%d\n",ans);
					return 0;
				}
			}
		} else
		if (mina == s){
			for (int i = 0; i <= m; ++i){
				if (f[i] >= s && f[i] <= minb){
					ans = 2 * (yy - s + k);
					printf("%d\n",ans);
					return 0;
				}
			}
		}		
	 	ans = (yy - xx + k)	* 2;
	}
	printf("%d\n",ans);
	return 0;
}

B 吃豆豆

    f[i][j][k] 代表 k 时间,走到了 i,j,吃到了多少豆豆。
    i j 可以从上下左右和本身推导过来。

我写的时候,是从当前点推导到其余的五个点。

一开始 f 全部赋值为 -1, 起点为 0.

 

#include<bits/stdc++.h>
using namespace std;
const int N = 20000;
int f[15][15][N],t[16][16];
int dx[5] = {-1,1,0,0,0};
int dy[5] = {0,0,-1,1,0};
int xs,ys,xt,yt,n,m,K;
int main(){
	scanf("%d%d%d",&n,&m,&K);
	memset(f,-1,sizeof(f));
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j) 
			scanf("%d",&t[i][j]);
	scanf("%d%d%d%d",&xs,&ys,&xt,&yt);
	int tx,ty;

	f[xs][ys][0] = 0;
	for (int k = 1; k <= N; ++k){
		for (int i = 1; i <= n; ++i)
			for (int j = 1; j <= m; ++j)
				if (f[i][j][k-1] != -1){
					for (int it = 0; it < 5; ++it){
						tx = i + dx[it]; ty = j + dy[it];
						if (t[tx][ty] != 0 && (k % t[tx][ty] == 0)){
							f[tx][ty][k] = max(f[tx][ty][k],f[i][j][k-1] + 1);
						} else {
							f[tx][ty][k] = max(f[i][j][k-1],f[tx][ty][k]);
						}
					}
				}
		if (f[xt][yt][k] >= K){
			printf("%d\n",k);
			return 0;
		}
	}
				
	return 0;
}

 

C 拆拆拆数

求读入的两个数是不是互质的,如果是就直接输出来。

不然。

暴力枚举前5个数,就一定会有答案出来。我也不知道为什么。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
long long gcd(long long a, long long b){
	if(b== 0) return a; else return gcd(b,a%b);
}
int main(){
	int T;
	LL n,m;
	scanf("%d",&T);
	while(T--){
		scanf("%lld%lld",&n,&m);
		if (gcd(n,m)==1){
			puts("1");
			printf("%lld %lld\n",n,m);
		} else{
			bool vis = 0;
			for (LL i = 2; i<= 5; ++i){
				for (LL j = 2; j <= 5; ++j)
					if (gcd(i,j)==1 && gcd(n-i,m-j) == 1){
						vis = 1;
						puts("2");
						printf("%lld %lld\n",i,j);
						printf("%lld %lld\n",n-i,m-j);
						break;
					}
				if (vis) break;
			}
		}
	}
	return 0;
}

 

E 流流流动

这个是一个树形DP 的题。

我们把所有点连起来之后。成为的不是一棵树。而是多棵树。对树做DP 即可。

// f[x] 代表我选了 x 的子树最大值。 g[x] 代表我不选 x 的子树最大值。
// 更新f g 的时候,用到了f g 的子树。
#include<bits/stdc++.h>
using namespace std;
const int N = 2000;
vector<int>p[N];
int n,m,ans,a[N],b[N],f[N],g[N];
bool vis[N];
void dfs(int x, int fa){
	f[x] = a[x]; g[x] = 0; vis[x] = 1;
	int psize = p[x].size();
	for (int i = 0; i < psize; ++i){
		int v = p[x][i];
		if (v == fa) continue;
		dfs(v,x);
		f[x] += max(f[v]-b[min(x,v)],g[v]);
		g[x] += max(f[v],g[v]);
	}
}

int main(){
	scanf("%d",&n);
	for (int i = 1; i <= n; ++i) scanf("%d",&a[i]);
	for (int i = 1; i <= n; ++i) scanf("%d",&b[i]);
	for (int i = 2; i <= n; ++i){
		if (i & 1){
			if (3*i+1 <= n){
				p[i].push_back(3*i+1);
				p[3*i+1].push_back(i);
			}
		} else{
			p[i].push_back(i/2);
			p[i/2].push_back(i);
		}
	}
	int ans;
	for (int i = 1; i <= n; ++i)
		if (!vis[i]){
			dfs(i,0);
			ans += max(g[i],f[i]);
		}
	printf("%d\n",ans);

	return 0;
}

 F 爬爬爬山

直接按照 dij 算法来就可以了

如果遇到有高的山, 再加上另外一个权值。,  (h[i] - h[1] - k)* (h[i] - h[1] - k) 

这个题注意  long long

#include<bits/stdc++.h>
using namespace std;
typedef pair<long long,int> P; //
const int N = 2e5+100;
int a[N],n,m,k,t;
int Head[N],Next[N*2],To[N*2],cnt = 0,val[N*2];
long long dis[N];
bool vis[N];
priority_queue<P,vector<P>, greater<P> > q; //小根堆。
void add(int u, int v, int w){
	++cnt;
	Next[cnt] =  Head[u];
	To[cnt] = v;
	val[cnt] = w;
	Head[u] = cnt;
}

void dij(){
	for (int i = 0; i <= n; ++i) dis[i] = 1e13;
	dis[1] = 0; q.push(P(0,1));
	while(!q.empty()){
		int u = q.top().second;  //找到dis 最小的起点。
		q.pop();
		if (vis[u]) continue;
		vis[u] = 1;   //这个起点已经更新过了。
		for (int i = Head[u]; i; i = Next[i]){ // 更新之后的点。
			int v = To[i];
			long long w = val[i];
			if (a[v] > t) w += 1ll*(a[v]-t)*(a[v]-t);  //这个是另外加的权值。
			if (dis[v] > dis[u] + w){
				dis[v] = dis[u] + w;
				q.push(P(dis[v],v));
			}
		}

	}
	return;
}

int main(){
	int x,y,z;
	scanf("%d%d%d",&n,&m,&k);
	for (int i = 1; i <= n; ++i) scanf("%d",&a[i]);
	for (int i = 0; i < m; ++i){
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z); add(y,x,z);
	}
	t = a[1] + k;  //判断能够达到最高的山高。
	dij();
	printf("%lld\n",dis[n]);

	return 0;
}

I 起起落落

f[i] 代表以 i 结尾的序列有多少个。 

然后我们两重循环暴力。

开一个变量 j 从 i - 1 向前循环,如果  a[j] < a[i]  那么 k ++, 

否则就是  a[j] > a[i] , 那这样,, a[j]  (中间的 k 个数) a[i] 就 又可以构成一个新的序列。

可以得到  f[i] += ( f[j] + 1 ) * k;

答案累加就可以了。 

 

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
int ans,n,m,k,a[2300];
long long f[2300];
int main(){
	scanf("%d",&n);
	for (int i = 1; i <= n; ++i)
		scanf("%d",&a[i]);
	for (int i = 3; i <= n; ++i){
		k = 0;
		for (int j = i-1; j > 0; j--)
		if (a[j] < a[i]) k++; else{
			f[i] = (f[i] + (1ll*(f[j]+1) * k)%mod) % mod;
		}	
		ans = (ans + f[i]) % mod;
	}
	printf("%d\n",ans);

	return 0;
}

 

J 夺宝奇兵

暴力枚举要的宝箱数 k ,每个人,如果有多于 k 个宝箱,就买下来那些多于 k 的宝箱,当然是从小到大买。

如果我还不够 k 个宝箱。那就从剩下的宝箱中 从小到大买。直到等于k 个宝箱。

 

#include<bits/stdc++.h>
using namespace std;
const int N = 2000;
typedef pair<int,int> P;
vector<P>g,f[N]; //g 总的宝箱。 f 每个人的宝箱、
bool vis[N];
long long ans,sum;
int n,m;
void init(){
	int x,y;
	scanf("%d%d",&n,&m);
	for (int i = 0; i < m; ++i){
		scanf("%d%d",&x,&y);
		g.push_back(P(x,i));
		f[y].push_back(P(x,i));
	}

	sort(g.begin(),g.end());
	for (int i = 1; i <= m; ++i)
		sort(f[i].begin(),f[i].end());
}

void solve(int k){
	int tt,t= 0;
	sum = 0;
	for (int i = 1; i <= m; ++i){
		tt = f[i].size() - k;
		for (int j = 0; j < tt; ++j){
			sum += f[i][j].first;
			vis[f[i][j].second] = 1;
			t++;
		}
	}

	tt = g.size();
	for (int i = 0; i < tt; ++i)
		if (vis[g[i].second] == 0){
			if (t <= k) t++; else break;
			sum += g[i].first;
		}
	ans =  min(ans,sum);
}

int main(){
	init();
	ans = 1e13;
	for (int i = m; i > 0; i--){
		for (int j = 0; j <= m; ++j) 
			vis[j] = 0;
		solve(i);
	}

	printf("%lld\n",ans);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值