CodeTON Round 8 (Div. 1 + Div. 2) (A~D)

文章讨论了四个编程竞赛题目,涉及排序、区间选择、动态规划等技巧。A题要求对数组重新排列以保持有序;B题模拟求解MEX;C1/C2是关于生日蛋糕上的点选择优化问题;D题讲解了如何使用优先队列和动态规划解决绘画问题中的最大值选择。
摘要由CSDN通过智能技术生成

比赛地址:https://codeforces.com/contest/1942
传送门

A. Farmer John’s Challenge

很显然,将最后一位数放到第一位数还能使数组保持有序当且仅当所有数相等。所以要么 k = 1 或 n,要么无解

void solve()
{
    int n,k;
    cin>>n>>k;
    if(n==k) {
        while(n--) cout<<1<<' ';
    } else if(k==1) {
        for(int i=1;i<=n;i++) cout<<i<<' ';
    } else {
        cout<<-1;
    }
    cout<<'\n';
}

B. Bessie and MEX

由于保证有解,根据题目意思模拟即可

void solve()
{
	int n;
	cin>>n;
	vector<int> v(n);
	vector<int> st(n+1);
	for(auto &i: v) cin>>i;
	int last=0;
	for(auto p: v) {
		if(last-p>=0&&!st[last-p]) {
			// cout<<'a';
			cout<<last-p<<' ';
			st[last-p]=true;
		} else {
			// cout<<'b';
			cout<<last<<' ';
			st[last]=true;
			while(st[last]) last++;
		}
	}
	cout<<'\n';
}

C1. Bessie’s Birthday Cake (Easy Version)

对点排序,将他选的x个点相邻两个两两相连一定是最优解,(因为对于一个x个点的多边形,能够构成的三角形的最大个数为 x - 2,那么点越多三角形就越多)。再看他选的每两个点之间是否夹着一个点,每有一个答案++(因为这两个点连成的边与蛋糕的边缘也能构成一个三角形)

void solve()
{
	int n,x,y;
	cin>>n>>x>>y;
	vector<int> v(x);
	for(auto &i: v) cin>>i;
	sort(v.begin(),v.end());
	ll ans=x-2;
	for(int i=1;i<x;i++) {
		if(v[i]-v[i-1]==2) ans++;
	}
	if((v[x-1]+2)%n==v[0]) ans++;
	
	cout<<ans<<'\n';
}

C2. Bessie’s Birthday Cake (Hard Version)

C1的复杂版,主角可以额外选 y 个点。在多边形中的两个已选择的点中间间隔了一个点,显然这个点选不选对答案没有影响,称之无效,在两个已选择的点中间间隔了x(x>1)个点,每间隔一个点选一个点,这样的点可以使答案++,称之有效。在此基础之上,选择奇数个点的区间比偶数个点的区间(当剩余可选择点数量足够时)最后构成三角形的数量更多,所以优先考虑奇数点区间。
别忘了最后一个点和第一个点之间的区间。

void solve()
{
	int n,x,y;
	cin>>n>>x>>y;
	vector<int> v(x);
	for(auto &i: v) cin>>i;
	
	ll ans=x-2;
	sort(v.begin(),v.end());
	vector<int> _diff;
	for(int i=1;i<x;i++) {
		if(v[i]-v[i-1]>2)
			_diff.push_back(v[i]-v[i-1]-1);
		else if(v[i]-v[i-1]==2) {
			ans++;
		}
	}
	
	if(v[0]+n-v[x-1]>2) {
		_diff.push_back(v[0]+n-v[x-1]-1);
	} else if(v[0]+n-v[x-1]==2) {
		ans++;
	}
	
	sort(_diff.begin(),_diff.end(),[&](int x,int y){return x%2&&y%2?x<y:x%2;});
	for(auto diff: _diff) {
		// cout<<diff<<' ';
		if(diff%2) {
			if(diff/2<=y) {
				y-=diff/2;
				ans+=(diff/2)*2+1;
			} else {
				ans+=y*2;
				y=0;
			}
		} else {
			if(diff/2<=y) {
				y-=diff/2;
				ans+=(diff/2)*2;
			} else {
				ans+=y*2;
				y=0;
			}
		}
	}
	// cout<<'\n';
	cout<<ans<<'\n';
}

D. Learning to Paint

不知道是什么dp
维护dp[i][k] 数组表示考虑前 i 个格子的第 k 大数,对于枚举的第 i 个点,有下面三种情况:

  • 不选第 i 个数,即 dp[i-1][0]
  • 选择第 i 个数,需要考虑包含第 i 个数组的区间的左边界,假设左边界为 j+2,即dp[j][0] + v[j+2][i]
  • 选择从1开始到i区间的所有格子,即 v[1][i]

需要将大的数优先放进dp[i] 中,所以使用优先队列,对于每个区间,枚举更新第 k + 1大的结果放入优先队列中。

void solve()
{
	int n,k;
	cin>>n>>k;
	vector v(n+1,vector<int>(n+1));
	for(int i=1;i<=n;i++) {
		for(int j=i;j<=n;j++) {
			cin>>v[i][j];
		}
	}
	
	vector dp(n+1,vector<int>());
	dp[0].push_back(0);
	for(int i=1;i<=n;i++) {
		priority_queue<array<int,3>> pq;
		pq.push({dp[i-1][0],i-1,0});
		pq.push({v[1][i],-1,0});
		for(int j=i-2;j>=0;j--) {
			pq.push({dp[j][0]+v[j+2][i],j,0});
		}
		
		while(!pq.empty()&&dp[i].size()<k) {
			auto [val,j,num]=pq.top();
			pq.pop();
			dp[i].push_back(val);
			if(j<0||num+1==dp[j].size()) continue;
			if(j==i-1) {
				pq.push({dp[j][num+1],j,num+1});
			} else {
				pq.push({dp[j][num+1]+v[j+2][i],j,num+1});
			}
		}
	}
	
	for(auto temp: dp[n]) {
		cout<<temp<<' ';
	}
	cout<<'\n';
}
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值