Codeforces Round #787 (Div. 3) ABCDEF

文章目录

  • 一、A. Food for Animals?
  • 二、B - Make It Increasing
  • 三、C - Detective Task
  • 四、D - Vertical Paths
  • 五、E - Replace With the Previous, Minimize
  • 六、F. Vlad and Unfinished Business
  • 总结


一、A. Food for Animals?

思路 因为猫粮和狗粮是固定的,所以先让猫吃猫粮,狗吃狗粮,然后如果猫粮或者狗粮不够的话,再让他们吃共同粮,判断一下共同的粮食够不够吃

#include 
#define int long long
#define ios ios::sync_with_stdio(false);cin.tie(0)
#define read(i,l,r) for(int i = l;i <= r;i ++ )
#define pb push_back
#define all(x) x.begin(),x.end()
#define endl '\n'
using namespace std;
const int N = 1100,mod = 1000000007;

void solve()
{
	int a,b,c,x,y; cin >> a >> b >> c >> x >> y;
	
	int q = max(x - a,(int)0);
	int w = max(y - b,(int)0);
	if(q + w <= c)
	cout << "YES" << endl;
	else
	cout << "NO" << endl;
}

signed main()
{
	ios; int t; cin >> t;
	
	while( t -- ) solve();

	

	return 0;
} 

二、B - Make It Increasing

思路 因为每次都是a[i]除以2 所以,越除越小,所以从后面往前面遍历,满足贪心

#include 
#define int long long
#define ios ios::sync_with_stdio(false);cin.tie(0)
#define read(i,l,r) for(int i = l;i <= r;i ++ )
#define pb push_back
#define all(x) x.begin(),x.end()
#define endl '\n'
using namespace std;
const int N = 1100,mod = 1000000007;
int a[N];

void solve()
{
	int n; cin >> n;
	for(int i = 1;i <= n;i ++ )
	{
		cin >> a[i];
	}
	int ans = 0;
	for(int i = n - 1;i >= 1;i -- )
	{
		if(a[i] < a[i + 1]) continue;
		if(a[i] == 0 || a[i + 1] == 0) 
		{
			cout << "-1" << endl;
			return;
		}
		
		
	while(a[i] >= a[i + 1])
	{
		a[i] /= 2;
		ans++;
	}
	
	}
	
	cout << ans << endl;

}

signed main()
{
	ios; int t; cin >> t;
	
	while( t -- ) solve();

	

	return 0;
} 

三、C - Detective Task

思路: 因为只有一个小偷,所以可以枚举每一个人,假设第i个人是小偷,那后面的人只能说实话,后面的人只能是问号或者0,第i个人前面的人也是得说实话所以只能是问号或者1,这样这个人才有可能是小偷

#include 
#define int long long
#define ios ios::sync_with_stdio(false);cin.tie(0)
#define read(i,l,r) for(int i = l;i <= r;i ++ )
#define pb push_back
#define all(x) x.begin(),x.end()
#define endl '\n'
using namespace std;
const int N = 2e5 + 100,mod = 1000000007;
int s1[N],s0[N],s[N];// 预处理一下
char a[N];
void solve()
{
	cin >> a + 1;
	int q = strlen(a + 1);
	for(int i = 1;i <= q;i ++ )
	{
		if(a[i] == '1')
		{
			s1[i] = s1[i - 1] + 1;
			s0[i] = s0[i - 1];
			s[i] = s[i - 1];
		}
		else if(a[i] == '0')
		{
			s1[i] = s1[i - 1];
			s0[i] = s0[i - 1] + 1;
			s[i] = s[i - 1];
		}
		else
		{
			s1[i] = s1[i - 1];
			s0[i] = s0[i - 1];
			s[i] = s[i - 1] + 1;
		}
		
	}
	int ans = 0;
	for(int i = 1;i <= q;i ++ )
	{
		if(s[i - 1] + s1[i - 1] == i - 1 && s[q] - s[i] + s0[q] - s0[i] == (q - i)) ans ++;  
		//如果前面全是1和问号,后面全是0和问号,那i就有可能是小偷
	}

	
	cout << ans << endl;
	
}

signed main()
{
	ios; int t; cin >> t;
	
	while( t -- ) solve();

	

	return 0;
} 

四、D - Vertical Paths

思路: 这个要路径最少的话,就看这颗树有多少个叶子节点,就有多少条路径,遍历一遍树存一下每个节点的父节点和叶子节点,每个节点只能用一次,然后再遍历叶子节点往上找即可

#include 

#define ios ios::sync_with_stdio(false);cin.tie(0)
#define read(i,l,r) for(int i = l;i <= r;i ++ )
#define pb push_back
#define all(x) x.begin(),x.end()
#define endl '\n'
using namespace std;
const int N = 2e5 + 100,mod = 1000000007;
int pre[N];
vector<int> res[N],ans;
bool st[N],st2[N];
vector<int> num;

void dfs(int u,int fa) // 无向图,但是我们只走一遍,fa判断是否重复走了
{
	
	for(int i = 0;i < res[u].size();i ++ )
	{
		int j = res[u][i];
		if(j == fa) continue;
		dfs(j,u);
		pre[j] = u;
	}
	
	if(res[u].size() == 1 && !st2[u])
	{
	    st2[u] = true;
	   
	    ans.pb(u);
	}
	
}

void solve()
{
	int n;	cin >> n;
	int root;
	read(i,1,n)
	{
		int x;
		cin >> x;
		if(x == i)
		root = i;
		res[x].pb(i);
		res[i].pb(x);
	}
	
	if(n == 1)
	{
	 cout << "1" << endl << "1" << endl << "1" << endl;
	 
	}
	else
	{
	   dfs(root,-1);
	
	cout << ans.size() << endl;
	
	for(int i = 0;i < ans.size();i ++ )
	{
		int u = ans[i]; // 往上面找
		
		num.pb(u);
		st[u] = true;
		
		while(1)
		{
			u = pre[u];
			if(u == 0) break;
			if(st[u]) break;
			else
			{
				st[u] = true;
				num.pb(u);
			}
			
		}
		
		cout << num.size() << endl;
		
		for(int i = num.size() - 1;i >= 0;i -- )
		cout << num[i] << ' ';
		
		cout << endl;
		num.clear();
		
		
	}
	}
	

	
	for(int i = 0;i <= n;i ++ )
	{
		st[i] = false;
		st2[i] = false;
		res[i].clear();
		pre[i] = 0;
	}
	
	ans.clear();
	
	
}

int main()
{
	ios; int t; cin >> t;
	
	while( t -- ) solve();
	return 0;
} 

五、E - Replace With the Previous, Minimize

思路: 这道题要求字典序最小,所以我们要从左到右判断每一个字符是否能到 ‘a’,如果该字符能到’a’,那后面的比该字符小的也全部能到’a’,当我们遍历到一个不符合条件的也就是说 k 次满足不了该字符变成 ‘a’,那我们就不用往后面遍历了,因为我们要满足字典序最小 所以最后多余的几次 k - w次就全部给这个字符 (w是使得前面的字符能够减到’a’需要的最大次数),就是让最这个字符减去(k - w)次

#include 
#define int long long
#define ios ios::sync_with_stdio(false);cin.tie(0)
#define read(i,l,r) for(int i = l;i <= r;i ++ )
#define pb push_back
#define all(x) x.begin(),x.end()
#define endl '\n'
using namespace std;
const int N = 2e5 + 100,mod = 1000000007;

void solve()
{
	int n,k; cin >> n >> k;
	string s; cin >> s;
	
	char op = 'a',w = s[0],op2 = 'a',u = 'a';
	string res;
	int cnt = 0;
	
	for(int i = 0;i < n;i ++ )
	{
		if((int)(s[i] - 'a') <= k) // 都能变成a 
		{
			w = max(s[i],w);// 找到一个能够到'a',最大的s[i] 
		}
		else
		{
		    if(i == 0)// 特判一下第一个字符,第一个字符到不了a,那就把k次全部给第一个字符
		    {
		     op = (char)((s[i] - 'a' - k) + 'a'); 
		     break;
		    }
		     
			 cnt = w - 'a'; 
			 cnt = k - cnt; // 最多减几次
			 int e = (int)(s[i] - 'a' - cnt);
			 op2 = (char)(e + 'a'); // 剩下的次数的全部给这个字符,使得这个字符减到了op2
			 u = s[i]; 
			 break;
		} 
	}

	for(int i = 0;i < n;i ++ )
	{
		if(s[i] <= w)
		res += min(op,s[i]);
		else
		{
			if(s[i] <= u)
			res += min(op2,s[i]);
			else
			res += s[i];
		}
	}
	
	cout << res << endl;
}

signed main()
{
	ios; int t; cin >> t;
	
	while( t -- ) solve();

	

	return 0;
} 

六、F. Vlad and Unfinished Business

思路: 先dfs遍历一遍树,存一下节点的前驱节点,然后标记一下从x到y这条路径中的所有的点,再遍历每一个需要去的节点的位置,让这个节点往上走,找到第一个被标记的节点,详细在代码中

#include 
#define int long long
#define ios ios::sync_with_stdio(false);cin.tie(0)
#define read(i,l,r) for(int i = l;i <= r;i ++ )
#define pb push_back
#define all(x) x.begin(),x.end()
#define endl '\n'
using namespace std;
const int N = 2e5 + 100,mod = 1000000007;
vector<int> res[N];
int a[N],pre[N],n,k,x,y,ans = -1;
bool st[N];

void dfs(int u,int fa,int len)
{
	if(u == y && ans == -1)
	{
		ans = len;
	}
	
	read(i,0,res[u].size() - 1)
	{
		int j = res[u][i];
		if(fa == j) continue;
		dfs(j,u,len + 1);
		pre[j] = u;
	}
	
}
void solve()
{
	scanf("%lld%lld%lld%lld",&n,&k,&x,&y);
	for(int i = 1;i <= k;i ++ ) scanf("%lld",&a[i]);
	
	read(i,1,n - 1)
	{
		int u,v;scanf("%lld%lld",&u,&v);
		res[u].pb(v);
		res[v].pb(u);
	}
	
	dfs(x,-1,0); // 先让x走到y;走了几步,因为存的是无向图,所以可以说这里面把x当成根了
	int q = y;
	st[y] = true; // 标记一下从x到y的所有路径
	while(q != x)
	{
		q = pre[q];
		st[q] = true;
	} 

	for(int i = 1;i <= k;i ++ ) // 遍历每一个节点
	{
		int d = 0;
		int q = a[i];  // 往上找,找到第一个被标记过的节点就结束
		//因为被标记过的节点,之前一定会被走
		while(1)
		{
			if(st[q]) break;	
			st[q] = true;
			q = pre[q];
			d++;
		}
		
		ans += (2 * d);// 来回是两步,所以就 d * 2
		
	}
	
	printf("%lld\n",ans);
	
	for(int i = 0;i <= n;i ++ )
	{
		res[i].clear();
		st[i] = false;
		pre[i] = 0;
	}
	
	ans = -1;
}

signed main()
{
	 int t; scanf("%lld",&t);
	while( t -- ) solve();

	return 0;
} 

总结

这个cf 打的不好,有点瓜皮了,自己,还是太菜

你可能感兴趣的:(c++,算法,贪心算法)