【解题报告】CF DIV2 #ROUND 718 A~D

【解题报告】CF DIV2 #ROUND 718 A~D

比赛链接
突然想起来自己上周打的还没补完题目(大雾)
开的虚拟,不算如意的一场,又被B题卡了,5分钟切A然后B卡很久都没写出来,后面花了20来分钟切C,5000+排名

A. Sum of 2050

思路
看有没有解直接看能不能整除就行,最少需要多少,看成2050进制数,把每位加起来即可
代码

// Problem: A. Sum of 2050
// Contest: Codeforces - Contest 2050 and Codeforces Round #718 (Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1517/problem/A
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// FishingRod

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
#define MULINPUT
/*DATA & KEY
t 1 1000
n 1 1e18
*/
int T;
void solve(int C)
{
	//NEW DATA CLEAN
	
	//NOTE!!!
	LL n;cin>>n;
	LL ans=0;
	if(n%2050==0)
	{
		LL k=n/2050;
		while(k)
		{
			ans+=k%10;
			k/=10;
		}
		cout<<ans<<endl;
	}
	else puts("-1");
	
	
}

int main()
{
	#ifdef MULINPUT
		scanf("%d",&T);
		for(int i=1;i<=T;i++)solve(i);
	#else
		solve(1);
	#endif
	return 0;
}

B. Morning Jogging

在这里插入图片描述

思路
模拟题,但谜之写不出来,参考这位大佬的题解
先把所有数里最小的m个确定下来,,其他的该在哪组就放在哪组
开个结构体把原来位置记录下来
代码

// Problem: B. Morning Jogging
// Contest: Codeforces - Contest 2050 and Codeforces Round #718 (Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1517/problem/B
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// FishingRod

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
#define MULINPUT
/*DATA & KEY

*/
int T;
const int N=105;
struct Node{int x,y,w;}r[N*N];
bool vis[N][N];
int ans[N][N],u[N][N],b[N][N];
bool cmp(Node a,Node b){return a.w<b.w;}
void solve(int C)
{
	//NEW DATA CLEAN
	memset(r,0,sizeof r);
	memset(vis,0,sizeof vis);
	memset(u,0,sizeof u);
	memset(ans,0,sizeof ans);
	memset(b,0,sizeof b);
	//NOTE!!!
	int n,m;cin>>n>>m;
	int cnt=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			cin>>r[++cnt].w;
			r[cnt].x=i;
			r[cnt].y=j;
			b[i][j]=r[cnt].w;
		}
	sort(r+1,r+1+cnt,cmp);
	for(int i=1;i<=m;i++)
	{
		ans[r[i].x][i]=r[i].w;
		vis[r[i].x][r[i].y]=1;//标记已经用掉的
	}
	
	for(int i=1;i<=n;i++)
	{
		int cnt=0;
		for(int j=1;j<=m;j++)
		{
			if(!vis[i][j])u[i][++cnt]=b[i][j];//u存储可以用的
		}
	}
	
	for(int i=1;i<=n;i++)
	{
		int cnt=0;
		for(int j=1;j<=m;j++)
		{
			if(!ans[i][j])ans[i][j]=u[i][++cnt];
		}
	}
	
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			cout<<ans[i][j]<<" ";
			if(j==m)puts("");
		}
}

int main()
{
	#ifdef MULINPUT
		scanf("%d",&T);
		for(int i=1;i<=T;i++)solve(i);
	#else
		solve(1);
	#endif
	return 0;
}

C - Fillomino 2

思路
答案一定有解,按照最优方法放置,先试试放左边,不行的话放下面。dfs实现即可
代码

// Problem: C. Fillomino 2
// Contest: Codeforces - Contest 2050 and Codeforces Round #718 (Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1517/problem/C
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// FishingRod

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
//#define MULINPUT
/*DATA & KEY

*/
int T;
const int N=510;
int g[N][N];
bool st[N][N];
int k;//放外面而不是作为参数
void dfs(int x,int y,int w)
{
	if(k==0)return;
	if(x<1||y<1||x<y||st[x][y])return;
	g[x][y]=w;k--;
	st[x][y]=1;
	dfs(x,y-1,w);
	dfs(x+1,y,w);
}
void solve(int C)
{
	//NEW DATA CLEAN
	
	//NOTE!!!
	int n;cin>>n;
	for(int i=1;i<=n;i++)cin>>g[i][i];
	for(int i=1;i<=n;i++)
	{
		int w=g[i][i];k=w;
		dfs(i,i,w);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)cout<<g[i][j]<<" ";
		puts("");
	}
}

int main()
{
	#ifdef MULINPUT
		scanf("%d",&T);
		for(int i=1;i<=T;i++)solve(i);
	#else
		solve(1);
	#endif
	return 0;
}

D. Explorer Space

在这里插入图片描述

思路
非常规输入,先搞明白输入吧。大概就是先告诉你横向边的边长,然后再告诉你纵向边的边长
在这里插入图片描述
样例
在这里插入图片描述

然后这题要干嘛呢?求出从(i,j)出发走k步,然后回到(i,j)经过路径和最小多少
那我们写个DP呗
状态表示
定 义 f ( i , j , k ) 为 从 i , j 走 k 步 最 小 答 案 定义f(i,j,k)为从i,j走k步最小答案 f(i,j,k)i,jk
状态计算
直接四个方向状态转移就可以了呀懒得写了
初始状态
f ( i , j , 0 ) f(i,j,0) f(i,j,0)
目标状态
f ( i , j , k / 2 ) f(i,j,k/2) f(i,j,k/2)折返了,因为可以重复走没影响那就反复走最小的路呗
答案直接乘2就可以了
如果是奇数就不可行输出-1
代码

// Problem: D. Explorer Space
// Contest: Codeforces - Contest 2050 and Codeforces Round #718 (Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1517/problem/D
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// FishingRod

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
//#define MULINPUT
/*DATA & KEY

*/
int T;
int n,m,k;
const int N=505;
map<PII,int>g;//用map不用g[N*N][N*N],不然爆空间
LL f[N][N][50];
int get_id(int x,int y){return (x-1)*m+y;}
bool check(int x,int y){if(x>=1&&x<=n&&y>=1&&y<=m)return 1;return 0;}
LL work(int x,int y,int s)//递归记忆化求解最值,这个写得比较少,记录一下
{
	if(f[x][y][s])return f[x][y][s];
	if(s==0)return f[x][y][0];
	LL tmp=1e18+7;//先设置一个临时变量极大值
	int u=get_id(x,y);
	if(check(x-1,y))tmp=min(tmp,work(x-1,y,s-1)+g[{u,get_id(x-1,y)}]);
	if(check(x+1,y))tmp=min(tmp,work(x+1,y,s-1)+g[{u,get_id(x+1,y)}]);
	if(check(x,y-1))tmp=min(tmp,work(x,y-1,s-1)+g[{u,get_id(x,y-1)}]);
	if(check(x,y+1))tmp=min(tmp,work(x,y+1,s-1)+g[{u,get_id(x,y+1)}]);
	return f[x][y][s]=tmp;//把临时变量作为答案,而不是直接f[x][y][s]放到min里
}
void solve(int C)
{
	//NEW DATA CLEAN
	
	//NOTE!!!
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
		for(int j=1;j<m;j++)
		{
			LL w;cin>>w;
			int u=get_id(i,j),v=get_id(i,j+1);//权不是点,而是边,可以二维转一维
			g[{u,v}]=g[{v,u}]=w;
		}
	for(int i=1;i<n;i++)
		for(int j=1;j<=m;j++)
		{
			LL w;cin>>w;
			int u=get_id(i,j),v=get_id(i+1,j);
			g[{u,v}]=g[{v,u}]=w;
		}
	if(k&1)
	{
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
			{
				printf("-1 ");
				if(j==m)puts("");
			}
		return;
	}
	
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			cout<<2*work(i,j,k/2)<<" ";
			if(j==m)puts("");
		}
}

int main()
{
	#ifdef MULINPUT
		scanf("%d",&T);
		for(int i=1;i<=T;i++)solve(i);
	#else
		solve(1);
	#endif
	return 0;
}

反思

A:

手速手速手速

B:

模拟题好像只能多练了,在必要的时候把原来信息(如左边都存下来)

C:

水题,貌似也没啥好讲的

D:

递归+记忆化求解最值:先设置临时变量存储所有转移最小答案,最终赋值f[x][y][[s]
折返找最小路径,如果已经走过的点是没有影响的话,那么直接重复两次最小路径即可,把目标状态设为一半
点-边权-点存图
法一:可以二维转一维+map优化空间(如果开数组注意转一维后N*N,而不是N)
顺便,二维转一维度的时候

(x-1)*m+y,不是(x-1)*n+y啊啊啊!

法二:横向边权和纵向边权分开存,转移的时候用不同的边权数组转移

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值