(Training 19)Codeforces Round #683

A. Add Candies

每次操作都会多增加1
非常简单的构造 如果一个位置一直不选择的话
m次构造会增加1+2+3+…+m (1+m)*(m)/2

只在第k次操作对i使用 那么这时候增加(1+m)*(m)/2-k
因此我们发现操作n次从1到n会使得全部相等

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int main(){
	int T;
	cin>>T;
	while(T--){
		int n;
		cin>>n;
		cout<<n<<endl;
		for(int i=1;i<=n;i++)
		printf("%d ",i);
		puts(" ");
	}
	return 0;
}

B. Numbers Box

画图发现如果有奇数个负数
那么我们取反后一定会让另一个正数变为负数 或者留下一个负数
那么我们计算出负数的个数 如果他为奇数那么我们取正数的最小值或者负数的最大值 他们中间的绝对值最小的这个数为负数 把所有的的绝对值的总和加起来减去这个数即为答案

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e3+10;
int a[N][N];
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
int main(){
	int T;
	cin>>T;
	while(T--){
		int n,m;
		scanf("%d%d",&n,&m);
		int sum=0,n1=0,n2=0,maxv=101,minv=-101;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				scanf("%d",&a[i][j]);
				sum+=abs(a[i][j]);
				if(a[i][j]<0){
					n1++; 
					minv=max(minv,a[i][j]);
				}
				else{
					maxv=min(maxv,a[i][j]);
				}
			}
		}
		if(n1&1){
			sum-=2*min(maxv,abs(minv));
			//cout<<min(maxv,abs(minv))<<" !!!!!"<<endl;
		}
			
		cout<<sum<<endl;
	}
	return 0;
}

C. Knapsack

题意:一个容量为w的背包 和n个物品 求出使得重量为W
使得(w+1)/2<=W<=w的一个方案 输出这个方案

思路:当物品的体积大于w时我们必然不选
而如果物品的体积为(w+1)/2<=Wi<=w 我们直接选择i即可
如果wi<(wi+1)/2时我们把它选上 当总和超过(w+1)/2时即为答案
这里不可能存在选择这个物品时小于(w+1)/2后超过w
因为当总体积超过(wi+1)/2就已经为答案了
2个数均小于(wi+1)/2时是不可能超过w的

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=2e5+10;
typedef long long LL;
int main(){
	int T;
	cin>>T;
	while(T--){
		int n;
		LL w;
		scanf("%d%lld",&n,&w);
		vector<int>ans;
		int flag=0,p=0;
		LL sum=0;
		for(int i=1;i<=n;i++){
			int x;
			scanf("%d",&x);
			if(x<=w){
				if(!flag)
				if(x>=(w+1)/2){
					p=i;
					flag=1;
				}
				else if(sum<(w+1)/2){
					sum+=x;
					ans.push_back(i);
					if(sum>=(w+1)/2)flag=1;
				}
			}
		}
		if(flag){
			if(p){
			cout<<1<<endl<<p;
			}
			else {
				cout<<ans.size()<<endl;
				for(int i=0;i<ans.size();i++)
				printf("%d ",ans[i]);
			}
			puts("");
		}
		else puts("-1");
			
	}
	return 0;
}

D. Catching Cheaters

题意:2个字符串A B选择出他们的子串C D
使得4*LCS(CD)-C-D最大 这里运算的值为长度
思路:普通的LCS的递推式f[i][j]=max(f[i-1][j],f[i][j-1]);

当ai==bi时 我们取f[i][j]=max(f[i][j],f[i-1][j-1]+1);
这道题用f[i][j]表示从 i,j 为2个字符串结尾
选出2个子串的最大值4*LCS(CD)-C-D
f[i][j]=max(f[i-1][j],f[i][j-1]);将会变为f[i][j]=max(f[i-1][j]-1,f[i][j-1]-1);因为LCS没变但是i和j增加了

当ai==bi时
我们这里取的时f[i][j]=max(f[i][j],f[i-1][j-1]+2);因为1个字符的LCS的长度为1后为4*LCS=4,i,j均增加1 故+2
最后转移过程中如果<0了我们把前面全部舍去 即让f[i][j]=0;

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e3+10;
int f[N][N];
int dp[N][N];
char str1[N],str2[N];
int main(){
	int n,m;
	cin>>n>>m;
	scanf("%s",str1+1);
	scanf("%s",str2+1);
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			f[i][j]=max({f[i-1][j]-1,f[i][j-1]-1,0});
			if(str1[i]==str2[j]){
				f[i][j]=max({f[i][j],f[i-1][j-1]+2});
				ans=max(ans,f[i][j]);
			}
		}
	
	}
	cout<<ans;
	return 0;
}

E. Xor Tree

题意:给出一个数组 其中每个元素回向异或值最小的点连边 形成一个
删除一些点后使得这个图变为一棵树 问最少删去多少点
思路:异或值最大时 我们都是从高位开始考虑 取反
这里要使得异或值最小我们依然要从高位考虑 我们要让异或值最小 必然最高位要相同的越多 那么异或值必然越小
我们写10个数 将他们转换为2进制
在这里插入图片描述

而这时候可以发现 从高位往下看 高位相同的数必然在一个大块里
而继续划分 又会按照高位相同的往下走 直到最后一位 从图上观察到
这里可以使用递归来进行分块
而块与块之间的关系的转移 也是临近的2个块 能连到一起
再观察 只有块里的数量为1时 才能进行转移相加
所以我们能从最高位30向0 进行递归 不断的将块向下进行分裂
然后在回溯的时候取能合并的点数的最大值
最终回溯的结果即为能形成所有的数一个块时的最大数 而总点数-最大值即为删除的最小值

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+10;
int a[N];
int f(int l,int r,int k){
	if(l>r)return 0;//没有数的情况 
	if(k<0)return r-l+1;
	int t=r;
	for(int i=l;i<=r;i++)
		if(a[i]&(1<<k)){
			t=i-1;
			break;
		}
	int L=f(l,t,k-1);
	int R=f(t+1,r,k-1);
	return max(L+min(r-t,1),R+min(t-l+1,1));
}
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	sort(a+1,a+1+n);
	int res=f(1,n,30);
	cout<<n-res;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值