2021年Robocom 复赛题解

题目

A:
**题意:  给定三个数,要求变成另外三个数。有两个操作,①任选一个数 + 40,其他两个数 -20;②任选一个数 -40,其他两个数 +20. 输出最少的操作次数,无解输出-1.

思路: 直接bfs暴力骗分(bushi)
①衍化操作3.通过操作1和操作2,可以使得某个数+60,另一个数-60,剩余的不变。
②判断可行解:
可以简化操作,都除以20,就变成+2,-1,-1.
 首先满足差值之和为0,dx + dy + dz = 0.
 而且差值都是20的倍数。
 而且需要 %3 同余,因为 2 == -1,1 == -2(模 3)
然后还是不会做。看了官方题解。
因为dx + dy + dz = 0,所以必定是一正两负或者两正一负。可以通过取负号变成一正两负。
那么就有 a < b < 0 < c.
先执行操作2,会导致b先到达0,至少是同时到达.因为操作不会改变三者的和,仍然是0.
那么就剩下a和c需要变了。此时满足-a = c且都是3的倍数,有解,否则无解。
 可以证明-a = c,因为操作过程中三者的和不变,a + b + c = 0,b = 0.
 可以证明a和c都是3的倍数。因为a、b、c在操作时始终%3相等,所以当b = 0,a和c%3也 = 0,都是3的倍数。
时间复杂度: O(1),思维题
代码:**

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<complex>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<unordered_map>
#include<list>
#include<set>
#include<queue>
#include<stack>
#define OldTomato ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr)
#define fir(i,a,b) for(int i=a;i<=b;++i) 
#define mem(a,x) memset(a,x,sizeof(a))
#define p_ priority_queue
// round() 四舍五入 ceil() 向上取整 floor() 向下取整
// lower_bound(a.begin(),a.end(),tmp,greater<ll>()) 第一个小于等于的
// #define int long long //QAQ
using namespace std;
typedef complex<double> CP;
typedef pair<int,int> PII;
typedef long long ll;
// typedef __int128 it;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const ll inf = 1e18;
const int N = 2e5+10;
const int M = 1e6+10;
const int mod = 1e9+7;
const double eps = 1e-6;
inline int lowbit(int x){ return x&(-x);}
template<typename T>void write(T x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        write(x/10);
    }
    putchar(x%10+'0');
}
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
int n,m,k,T;
void solve()
{
    int x,y,z; int a,b,c;
    cin>>a>>b>>c>>x>>y>>z;
    int t1 = a - x;
    int t2 = b - y;
    int t3 = c - z;
    if(t1 % 20 || t2 % 20 || t3 % 20 || t1+t2+t3 ) //预判
    {
    	cout<<-1<<"\n";
    	return ;
    }
    else 
    {
    	t1 /= 20; t2 /= 20; t3 /= 20;
    	int tmp = (t1 % 3 + 3) % 3;
    	if((t2 % 3 + 3) % 3 != tmp || (t3 % 3 + 3) % 3 != tmp) //同余3后相等
    	{
    		cout<<-1<<"\n";
    		return ;
    	}
    	else 
    	{
    		int tot = (t1 > 0) + (t2 > 0) + (t3 > 0);
    		if(tot == 2) t1 *= -1,t2 *= -1,t3 *= -1;
    		vector<int> va;
    		va.push_back(t1); va.push_back(t2); va.push_back(t3);
    		sort(va.begin(),va.end());
    		// for(int i=0;i<va.size();++i)
    		// {
    			// cout<<i<<" "<<va[i]<<endl;
    		// }
    		int ans = 0;
    		va[1] *= -1; //轮数
    		ans += va[1];
    		va[0] += va[1];
    		va[2] -= va[1] * 2;
    		ans += 2 * va[2] / 3;
    		
    		cout<<ans<<"\n";
    	}
    }
}
signed main(void)
{  
   // T = 1;
   OldTomato; cin>>T;
   // read(T);
   while(T--)
   {
   	 solve();
   }
   return 0;
}

B:
题意:  01背包裸题,但是背包容量m很大,但是价值很小。
思路:  ①两维互换,对于最大价值,看背包容积j最小取到多少,即是答案。
②赛场上的做法。对于m很大的情况,我直接贪心,按照性价比排序,从大到小装,当背包容积小于1e4的时候,对于剩余的物品套用01背包模板。n = 1e3,m = 1e4, O(nlogn + nm),可过。(代码比赛完不让看,不贴了,反正骗了满分。)
时间复杂度: O(n
m’), O(nlogn + n*m’)
代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<complex>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<unordered_map>
#include<list>
#include<set>
#include<queue>
#include<stack>
#define OldTomato ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr)
#define fir(i,a,b) for(int i=a;i<=b;++i) 
#define mem(a,x) memset(a,x,sizeof(a))
#define p_ priority_queue
// round() 四舍五入 ceil() 向上取整 floor() 向下取整
// lower_bound(a.begin(),a.end(),tmp,greater<ll>()) 第一个小于等于的
// #define int long long //QAQ
using namespace std;
typedef complex<double> CP;
typedef pair<int,int> PII;
typedef long long ll;
// typedef __int128 it;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const ll inf = 1e18;
const int N = 2e5+10;
const int M = 1e6+10;
const int mod = 1e9+7;
const double eps = 1e-6;
inline int lowbit(int x){ return x&(-x);}
template<typename T>void write(T x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        write(x/10);
    }
    putchar(x%10+'0');
}
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
#define int long long
int n,m,k,T;
int f[1001*30]; //价值不超过j,获得的最小的体积
int v[1001];
int w[1001];
int ans = 0;
void solve()
{
    read(n); read(m);
    int sum = 0;
    int tot = 0;
    for(int i=1;i<=n;++i)
    {
        read(v[i]);
        sum += v[i];
    }
    fir(i,1,n) read(w[i]),tot += w[i];
    if(m >= sum)
    {
    	ans = tot;
    	write(ans); return ;
    }
    mem(f,0x3f);
    f[tot] = sum;
    // f[0] = 0x3f3f3f3f;
    for(int i=1;i<=n;++i)
    {
    	for(int j=tot;j>=w[i];--j)
    	{
    		f[j] = min(f[j],f[j-w[i]]+v[i]); 
    	}
    }
    for(int i=tot;i>=0;--i)
    {
    	// cout<<i<<":"<<f[i]<<endl;
    	if(f[i] <= m)
    	{
    		ans = i;
    		break;
    	}
    }
    write(ans);
}
signed main(void)
{  
   T = 1;
   // OldTomato; cin>>T;
   // read(T);
   while(T--)
   {
   	 solve();
   }
   return 0;
}

C:
题意: 大模拟,敲了一小时没过样例。不做了,开摆。
D:
题意: 有n行m列的格子,构成一个地图。你有一个大本营,若干个防御堡垒,它们都有防御值s。有若干个僵尸队伍来打你的大本营,每个队伍有僵尸数量num.对于僵尸来说,他们走一个格子花费1秒;遇到一个堡垒时牺牲一个僵尸花费1秒,使该堡垒的防御值-1.当大本营被摧毁时,游戏结束,僵尸不再移动。所有僵尸队伍都会在开局选定一条伤亡最少的路线,如果路线有多条,选择最快到达的那条。要求输出最终的地图,如果大本营被摧毁,额外加一句"Game Over".
思路: 显然,可以把防御堡垒和大本营进行连边,对大本营跑一遍Dij,得到僵尸到它的最短路。然后僵尸按照这个最短路走。然后就是纯纯的模拟了,不多说。最后有一个点没过,因为防御堡垒剩余防御值 = 1,同时有三队僵尸,应该是三队都 -1,而不是第一个-1就把它摧毁了。懒得改了,敲就敲了半天。
时间复杂度: O(nm + VlogE),V = nm,E = V*4,需要先建边,n和m最大才100.也就是最多1e4个点。重点是模拟
代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<complex>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<unordered_map>
#include<list>
#include<set>
#include<queue>
#include<stack>
#define OldTomato ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr)
#define fir(i,a,b) for(int i=a;i<=b;++i) 
#define mem(a,x) memset(a,x,sizeof(a))
#define p_ priority_queue
// round() 四舍五入 ceil() 向上取整 floor() 向下取整
// lower_bound(a.begin(),a.end(),tmp,greater<ll>()) 第一个小于等于的
// #define int long long //QAQ
using namespace std;
typedef complex<double> CP;
typedef pair<int,int> PII;
typedef long long ll;
// typedef __int128 it;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const ll inf = 1e18;
const int N = 111;
const int M = 1e6+10;
const int mod = 1e9+7;
const double eps = 1e-6;
inline int lowbit(int x){ return x&(-x);}
template<typename T>void write(T x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        write(x/10);
    }
    putchar(x%10+'0');
}
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
struct node{
	int x,y; //去哪个边
	int w; //边权
	bool operator<(const node&rhs)
	{
		return w < rhs.w;
	}
};
int n,m,k,T;
int a[111][111];
int sx,sy;
vector<node> va[111][111]; //存图
PII pre[N][N];
void init()
{
    for(int i=0;i<=n+1;++i)
    {
    	for(int j=0;j<=m+1;++j)
    	{
    		if(i-1 >= 1) va[i][j].push_back({i-1,j,a[i-1][j]});
    		if(i+1 <= n) va[i][j].push_back({i+1,j,a[i+1][j]});
    		if(j-1 >= 1) va[i][j].push_back({i,j-1,a[i][j-1]});
    		if(j+1 <= m) va[i][j].push_back({i,j+1,a[i][j+1]});
    	}
    }	
}
struct node3
{
	int dis;
	int x,y;
	bool operator<(const node3&rhs)const
	{
		return dis > rhs.dis;
	}
};
int dist[N][N];
bool vis[N][N];
void Dij()
{
	for(int i=0;i<=105;++i)
	{
		for(int j=0;j<=105;++j)
		{
			pre[i][j] = {-1,-1};
			dist[i][j] = 0x3f3f3f3f;
			vis[i][j] = false;
		}
	}
	dist[sx][sy] = 0;
	priority_queue<node3> q;
	q.push({0,sx,sy});
	while(q.size())
	{
		auto tmp = q.top(); q.pop();
		int x = tmp.x,y = tmp.y;
		if(vis[x][y]) continue;
		vis[x][y] = 1;
		for(int i=0;i<va[x][y].size();++i)
		{
			int tx = va[x][y][i].x;
			int ty = va[x][y][i].y;
			// cout<<x<<" "<<y<<":"<<tx<<' '<<ty<<endl;
			if(dist[tx][ty] > dist[x][y] + va[x][y][i].w)
			{
				dist[tx][ty] = dist[x][y] + va[x][y][i].w;
				q.push({dist[tx][ty],tx,ty});
				pre[tx][ty].first = x;
				pre[tx][ty].second = y;
			}
		}
	}
}
vector<node3> js; //僵尸
void solve()
{
    cin>>n>>m>>k;
    for(int i=0;i<n+2;++i)
    {
    	for(int j=0;j<m+2;++j)
    	{
    		cin>>a[i][j];
    		if(a[i][j] < 0)
    		{
    			sx = i;
    			sy = j;
    		}
    	}
    }
    init();
//    for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) sort(va[i][j].begin(),va[i][j].end());
    Dij();
    
    for(int i=1;i<n+1;++i) 
    {
    	if(a[i][0] > 0)
    	{
    		js.push_back({a[i][0],i,0});
    		pre[i][0].first = i;
    		pre[i][0].second = 1;
    	}
    	if(a[i][m+1] > 0)
    	{
    		js.push_back({a[i][m+1],i,m+1});
    		pre[i][m+1] = {i,m};
    	}
    }
    for(int j=1;j<=m;++j)
    {
    	if(a[0][j] > 0)
    	{
    		js.push_back({a[0][j],0,j});
    		pre[0][j] = {1,j};
    	}
    	if(a[n+1][j] > 0)
    	{
    		js.push_back({a[n+1][j],n+1,j});
    		pre[n+1][j] = {n,j};
    	}
    }
    // cout<<js.size()<<"???"<<endl;
    bool flag = false;
    for(int t=1;t<=k;++t)
    {
    	for(int i=0;i<js.size();++i)
    	{
    		if(js[i].dis == 0)
    		{
    			continue;
    		}
    		int x = js[i].x; int y = js[i].y;
    		int tx = pre[x][y].first;
    		int ty = pre[x][y].second;
    		// if(i == 0) cout<<js[i].dis<<"->"<<x<<' '<<y<<":"<<tx<<' '<<ty<<"  "<<a[tx][ty]<<endl;
    		if(a[tx][ty] == 0)
    		{
    			js[i].x = tx;
    			js[i].y = ty;
    		}
    		else if(a[tx][ty] > 0)
            {
            	js[i].dis -- ;
            	a[tx][ty] -- ;
            }
            else 
            {
            	a[tx][ty] ++ ;
            	js[i].dis -- ;
            }
    	}
    	if(a[sx][sy] >= 0)
    	{
    		flag = true;
    		break;
    	}
    }
    for(int i=1;i<=n;++i)
    {
    	for(int j=1;j<=m;++j)
    	{
    		if(j > 1) cout<<' ';
    		cout<<a[i][j];
    	}
    	cout<<"\n";
    }
    if(flag) cout<<"Game Over";
}
signed main(void)
{  
   T = 1;
   // OldTomato; cin>>T;
   // read(T);
   while(T--)
   {
   	 solve();
   }
   return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值