CCPC-Wannafly Winter Camp Day1(div2)

  1. 机器人
  2. 吃豆豆
  3. 拆拆拆数
  4. 超难的数学题 –
  5. 流流流动
  6. 爬爬爬山
  7. 双重矩阵 –
  8. 我爱割葱 –
  9. 起起落落
  10. 夺宝奇兵
  11. 星球大战 –

机器人

思路:对于b区没有点的情况比较简单,找出a区中满足条件的最左边的右端点和最右边的左端点,再算下初始点s到这个区间的距离。b区也有点的话,大致就是找到一个最小的矩形包含所有的必经点,再计算下s到这个区间的距离。

#include<bits/stdc++.h>//机器人
using namespace std;

pair<int,int>p[100005];
int b[105];
int main(){
	int n,r,m,k,s;
	scanf("%d%d%d%d%d",&n,&r,&m,&k,&s);
	for(int i=1;i<=r;i++){
		scanf("%d%d",&p[i].first,&p[i].second);
	}
	for(int i=1;i<=m;i++){
		scanf("%d",&b[i]);
	}
	b[0]=1,b[m+1]=n;
	int l1=n,l2=s,r1=1,r2=s;//1是b区,2是a区
	bool gotob=false;
	sort(b,b+m+2);
	for(int i=1;i<=r;i++){
		if(p[i].second){
			gotob=true;
			l1=min(l1,*(upper_bound(b,b+m+2,p[i].first)-1));
			r1=max(r1,*lower_bound(b,b+m+2,p[i].first));
		}
	}
	b[m+2]=s;
	sort(b,b+m+3);
	for(int i=1;i<=r;i++){
		if(!p[i].second){
			l2=min(l2,*(upper_bound(b,b+m+3,p[i].first)-1));
			r2=max(r2,*(lower_bound(b,b+m+3,p[i].first)));
		}
	}
	int fl=min(l1,l2),fr=max(r1,r2);
	int ans=0;
	if(gotob){
		ans=2*k+fr-fl+r1-l1+l1-fl+fr-r1+(max(0,s-fr))*2+(max(0,fl-s))*2;
	}
	else{
		ans=(fr-fl)*2+(max(0,s-fr))*2+(max(0,fl-s))*2;
	}
	printf("%d\n",ans);
	return 0;
}

吃豆豆

思路:三维dp[x][y][t]表示位置(x,y)在t时刻最多可以吃到的豆。每个位置t时刻的状态由周围四个位置和自己当前位置的前一秒的状态转移过来(角落边界周围位置不是四个)。
如果当前位置是(x,y),旁边有(x+1,y)是合法位置,那么就有dp[x][y][t]=max(dp[x][y][t],dp[x+1][y][t-1] + t时刻在(x,y)能否吃到豆),其它都是同理。
直接枚举时间,枚举位置,枚举完位置,看看终点位置的当前时间的状态是否满足条件(大于c个豆)即可

#include<bits/stdc++.h>//吃豆豆
using namespace std;

int t[11][11];
int dp[11][11][10000];
int n,m,c;
int sx,sy,fx,fy;

int main(){
	scanf("%d%d%d",&n,&m,&c);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&t[i][j]);
	scanf("%d%d%d%d",&sx,&sy,&fx,&fy);
	int ans=0;
	memset(dp,-1,sizeof(dp));
	dp[sx][sy][0]=0;
	for(int i=1;;i++){
		for(int j=1;j<=n;j++)
			for(int k=1;k<=m;k++){
				if(k-1>=1&&dp[j][k-1][i-1]!=-1)dp[j][k][i]=max(dp[j][k][i],dp[j][k-1][i-1]+(i%t[j][k]==0?1:0));
				if(k+1<=m&&dp[j][k+1][i-1]!=-1)dp[j][k][i]=max(dp[j][k][i],dp[j][k+1][i-1]+(i%t[j][k]==0?1:0));
				if(j+1<=n&&dp[j+1][k][i-1]!=-1)dp[j][k][i]=max(dp[j][k][i],dp[j+1][k][i-1]+(i%t[j][k]==0?1:0));
				if(j-1>=1&&dp[j-1][k][i-1]!=-1)dp[j][k][i]=max(dp[j][k][i],dp[j-1][k][i-1]+(i%t[j][k]==0?1:0));
				if(dp[j][k][i-1]!=-1)dp[j][k][i]=max(dp[j][k][i],dp[j][k][i-1]+(i%t[j][k]==0?1:0));
			}
		if(dp[fx][fy][i]>=c){
			ans=i;
			break;
		}
	}
	printf("%d\n",ans);
	return 0;
}

n,m比较小,搜索也是可以的。

#include<bits/stdc++.h>//吃豆豆,搜索
using namespace std;

int t[11][11];
int dp[11][11][10000];
int n,m,c;
int sx,sy,fx,fy;
int dir[5][2]={0,0,1,0,0,1,-1,0,0,-1};

struct node{
	int x,y,t,v;//位置x,y,t:时间,v:吃到的豆子数
	node(){}
	node(int x,int y,int t,int v){
		this ->x=x;
		this ->y=y;
		this ->t=t;
		this ->v=v;
	}
};

void bfs(int &ans){
	queue<node>q;
	q.push(node(sx,sy,0,0));
	dp[sx][sy][0]=0;
	while(!q.empty()){
		node now=q.front();
		q.pop();
		if(now.x==fx&&now.y==fy&&now.v>=c){
			ans=now.t;
			return;
		}
		if(dp[now.x][now.y][now.t]>now.v)continue;
		for(int i=0;i<5;i++){
			int dx=now.x+dir[i][0];
			int dy=now.y+dir[i][1];
			if(dx<1||dx>n||dy<1||dy>m)continue;
			int dt=now.t+1;
			int dv=now.v+(dt%t[dx][dy]==0?1:0);
			if(dp[dx][dy][dt]>=dv)continue;
			dp[dx][dy][dt]=dv;
			q.push(node(dx,dy,dt,dv));
		}
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&c);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&t[i][j]);
	scanf("%d%d%d%d",&sx,&sy,&fx,&fy);
	memset(dp,-1,sizeof(dp));
	int ans;
	bfs(ans);
	printf("%d\n",ans);
	return 0;
}

拆拆拆数

思路:如果a,b互质直接输出答案即可。否则n=2,暴力找下前100个数字就能得到满足的答案。(不会证明)

#include<bits/stdc++.h>//拆拆拆数
using namespace std;

typedef long long ll;

int main(){
	int t;
	scanf("%d",&t);
	for(int ca=1;ca<=t;ca++){
		ll a,b;
		scanf("%lld%lld",&a,&b);
		if(__gcd(a,b)==1){
			printf("1\n%lld %lld\n",a,b);
		}
		else{
			printf("2\n");
			bool flag=false;
			for(ll i=2;i<=100;i++){
				for(ll j=2;j<=100;j++){
					if(__gcd(i,j)==1&&__gcd(a-i,b-j)==1){
						printf("%lld %lld\n%lld %lld\n",i,j,a-i,b-j);
						flag=true;break;
					}
				}
				if(flag)break;
			}
		}
	}
	return 0;
}

流流流动

思路:连好所有的边以后,就形成了一个森林,对于里面的一棵树,利用树形dp获得这棵树所能产生的最大价值。dp[u][k]表示u这个结点1.k=0,该点不取的最优情况。2.k=1,该点取的最优情况。刚访问到时初始化,回溯时儿子向根转移状态。把所有树的最优状态加起来就是整个森林的最优状态。

#include<bits/stdc++.h>//流流流动
using namespace std;

const int N=105;
int f[N],d[N];
bool vis[N];

int head[N],cnt;
struct e{
	int v,nxt;
}edge[N<<2];

void addedge(int u,int v){
	edge[++cnt].v=v;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}

int dp[N][2];

void dfs(int u,int fa){
	vis[u]=true;
	dp[u][0]=0;
	dp[u][1]=f[u];
	for(int i=head[u];~i;i=edge[i].nxt){
		int v=edge[i].v;
		if(v==fa)continue;
		dfs(v,u);
		dp[u][0]=max(dp[u][0]+dp[v][0],dp[u][0]+dp[v][1]);
		dp[u][1]=max(dp[u][1]+dp[v][0],dp[u][1]+dp[v][1]-d[min(u,v)]);
	}
}

int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&f[i]);
	for(int i=1;i<=n;i++)scanf("%d",&d[i]);
	memset(head,-1,sizeof(head));cnt=0;
	for(int i=2;i<=n;i++){
		if((i&1)&&(3*i+1)<=n){
			addedge(i,3*i+1);
			addedge(3*i+1,i);
		}
		else if(!(i&1)){
			addedge(i,i>>1);
			addedge(i>>1,i);
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			dfs(i,-1);
			ans+=max(dp[i][0],dp[i][1]);
		}
	}
	printf("%d\n",ans);
	return 0;
}

爬爬爬山

思路:对于一条费用为w的边u->v,如果v的高度大于初始高度+k,设高出h,那么就在建边的时候建一条费用为w+hh的u->v的边。反方向,如果u的高度大于初始高度+k,设高出x,那么就建一条费用为w+xx的v->u的边。然后跑个dijhstra之类的最短路算法就可以了

#include<bits/stdc++.h>//爬爬爬山
using namespace std;
typedef long long ll;
const int N=1e5+5;
const ll INF=1e17+7;
ll h[N];
ll head[N],cnt;
struct node{
	ll v,nxt,val;
}edge[N<<2];

void addedge(ll u,ll v,ll w){
	edge[++cnt].v=v;
	edge[cnt].val=w;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}

struct node2{
	ll id,val;
	node2(){}
	node2(ll id,ll val){
		this->id=id;
		this->val=val;
	}
	bool operator < (const node2 &a)const{
		return val>a.val;
	}
};
ll dist[N];
void dij(){
	priority_queue<node2>q;
	q.push(node2(1,0));
	dist[1]=0;
	while(!q.empty()){
		node2 now=q.top();
		q.pop();
		ll u=now.id;
		if(now.val!=dist[u])continue;
		for(ll i=head[u];~i;i=edge[i].nxt){
			ll v=edge[i].v;
			ll w=edge[i].val;
			if(dist[v]-w>dist[u]){
				dist[v]=dist[u]+w;
				q.push(node2(v,dist[v]));
			}
		}
	}
}

int main(){
	ll n,m,k;
	scanf("%lld%lld%lld",&n,&m,&k);
	for(ll i=1;i<=n;i++){
		scanf("%lld",&h[i]);
	}
	memset(head,-1,sizeof(head));
	cnt=0;
	fill(dist+1,dist+N,INF);
	ll u,v,w;
	for(ll i=1;i<=m;i++){
		scanf("%lld%lld%lld",&u,&v,&w);
		ll tmp=0;
		if(h[v]>h[1]+k){
			tmp=h[v]-h[1]-k;
		}
		addedge(u,v,w+tmp*tmp);
		tmp=0;
		if(h[u]>h[1]+k){
			tmp=h[u]-h[1]-k;
		}
		addedge(v,u,w+tmp*tmp);
	}
	dij();
	printf("%lld\n",dist[n]);
	return 0;
}

起起落落

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll mod=1e9+7;
int p[2005];
ll dp[2005];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&p[i]);
	}
	fill(dp,dp+1+n,1);
	ll tmp=0;
	for(int i=3;i<=n;i++){
		tmp=0;
		for(int j=i-1;j>=1;j--){
			if(p[j]<p[i])tmp++;
			else dp[i]=(dp[i]+dp[j]*tmp)%mod; 
		}
	}
	ll ans=0;
	for(int i=1;i<=n;i++){
		ans=(ans+dp[i]-1)%mod;
	}
	printf("%lld\n",ans);
	return 0;
}

夺宝奇兵

思路:枚举每个人剩下x个物品,超过x个物品的,记有一个人多了y件,那么wls买走这个最便宜的y件物品。如果这样最后还不够x+1,那么在剩余的物品中调便宜的买,直到够了为止

#include<bits/stdc++.h>//夺宝奇兵
using namespace std;
typedef long long ll;

const ll INF=1e18+7;
const int N=1005;
ll sz[N],sum[N];
priority_queue<ll>q[N];
multiset<ll>st;

int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	ll a,c;
	for(int i=1;i<=m;i++){
		scanf("%lld%lld",&a,&c);
		sz[c]++;
		sum[c]+=a;
		q[c].push(a);
	}
	ll ans=INF;
	for(int i=2;i<=m;i++){
		ll tmp=0,cnt=0;
		for(int j=1;j<=n;j++){
			if(!sz[j])continue;
			ll x=q[j].top();
			q[j].pop();
			sum[j]-=x;
			sz[j]--;
			tmp+=sum[j];
			st.insert(x);
			cnt+=sz[j];
		}
		if(cnt<i){
			for(multiset<ll>::iterator it=st.begin();it!=st.end()&&cnt<i;it++){
				tmp+=*it;
				cnt++;
			}
		}
		ans=min(ans,tmp);
	}
	printf("%lld\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值