CF Round #522 (Div. 2)

版权声明:转载请在原文附上源连接以及作者,谢谢~ https://blog.csdn.net/weixin_39778570/article/details/84317421

ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
题目链接:http://codeforces.com/contest/1079
官方题解:http://codeforces.com/blog/entry/63324

A题

题意:吃一种食物需要完整的一套餐具,每套餐具是是一样的,由不同的餐具组成,如刀子,叉子,筷子(由不同的数字表示,如1,2,3)。用餐过程会被偷掉一些餐具。现在知道最后剩下n个餐具,分别为a[1],a[2],a[3]…a[n],和k个客人,问最少被偷掉几个餐具
解法:最主要的点是算出食物的数目(fish),跟据餐具的套数算出最少的fish
fish的最少为 餐具套数/人数 (向上取整)
a n s = s u m f i s h k n ans = sum*fish*k-n (sum为一套餐具最少有几种餐具)

#include<bits/stdc++.h> 
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;

int n,k,a[200];
int main(){ 
	cin>>n>>k;
	int x;
	fo(i,1,n){
		scanf("%d",&x);
		a[x]++;
	}
	int mx= -1,sum=0;
	fo(i,1,100){
		if(a[i]) mx = max(mx,a[i]),sum++;
	}
	if(mx%k==0)mx/=k;
	else mx = mx/k +1;
	int ans = sum*mx*k - n;
	cout<<ans;
	return 0;
}

B题

题意:问一个字符串最少能分成几行,每列最多20个字符。形成一个矩阵,不为矩阵由*代替,两行差不能超过2个星
解法:算出最少有多少行,跟据行算出列,算出有多少个星星,输出,每行最多一个星星

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;

char s[106];
int main(){
	scanf("%s",s+1);
	int len = strlen(s+1);
	int row = len%20==0? len/20:len/20+1;
	int col = len%row==0? len/row:len/row+1;
	int xin = row * col - len;
	int t1 = 0, t2=1;
	cout<<row<<" "<<col<<endl;
	fo(i,1,row){
		int j = 1;
		if(t1++<xin)printf("*"),j++;
		for(j; j<=col; j++){
			printf("%c",s[t2++]);
		}
		printf("\n");
	} 
}

C题

题意:给定一个数列a,求数列b, 1 &lt; = b [ i ] &lt; = 5 1&lt;=b[i]&lt;=5
i f   a [ i ] &lt; a [ i + 1 ]   t h e n   b [ i ] &lt; b [ i + 1 ] if \ a[i]&lt;a[i+1] \ then \ b[i]&lt;b[i+1]
i f   a [ i ] &gt; a [ i + 1 ]   t h e n   b [ i ] &gt; b [ i + 1 ] if \ a[i]&gt;a[i+1] \ then \ b[i]&gt;b[i+1]
i f   a [ i ] = a [ i + 1 ]   t h e n   b [ i ] b [ i + 1 ] if \ a[i]=a[i+1] \ then \ b[i]≠b[i+1]
解法:
记忆化搜索,O(5n)
d p [ s t e p ] [ i ] = x dp[step][i]=x 表示第 s t e p 1 step-1 步的值为 x x , s t e p step 步的值为$i $
d f s ( s t e p , x ) dfs(step,x) 表示第 s t e p 1 step-1 步的值为x是否合法,合法返回 s t e p 1 step-1 的值

#include<bits/stdc++.h>
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;

int n,dp[100005][6],a[100005]; 
// dp[step][i]=x 表示第step-1步的值为x,step步的值为i 
// dfs(step,x) 表示第step-1步的值为x是否合法,合法返回step-1的值 
bool dfs(int step, int x){// step当前,x属于step-1 
	if(step>n) return dp[step][0]=x;
	if(dp[step][x]!=-1)return dp[step][x];
	if(a[step]>a[step-1]){
		fo(i,x+1,5){
			if(dfs(step+1,i))return dp[step][i]=x; // 搜对了才赋值,相当于回溯,但显然比回溯快 
		}										   // 因为回溯会破坏掉之前已经搜索过的错误解空间,导致重判 
	}else if(a[step]<a[step-1]){
		fo(i,1,x-1){
			if(dfs(step+1,i))return dp[step][i]=x;
		}
	}else{
		fo(i,1,5)if(i!=x){		
			if(dfs(step+1,i))return dp[step][i]=x;
		}
	}
	return dp[step][x]=0; // 上个值x搞错了,往后都不合法 
}
int b[100005];
int main(){
	cin>>n;
	fo(i,1,n)scanf("%d",&a[i]);
	memset(dp,-1,sizeof(dp));
	dfs(1,0); // 出口总返回0, 第0号元素的值 
	if(dp[n+1][0]!=-1){
		int t=n;
		for(int x=dp[n+1][0],i=n; i>=1; x=dp[i][x],i--){
			b[t--]=x; // 往回赋值 
		}
		fo(i,1,n)printf("%d%c",b[i],i==n?'\n':' ');
		return 0;
	}
	puts("-1");
	return 0;
}

D题

题意:给一个网格上的两个点a,b和一条直线 Ax+By+C=0。点a只能沿着网格或这直线走到b。问最小行走距离。
解法:枚举a不走直线到b。
a沿x轴走到直线到达点a1,a沿y轴走到直线到达点2
同理得b1,b2.
枚举 a --> a(1or2) --> b(1or2) --> b 的距离
取5种情况的最小值

#include<bits/stdc++.h> 
#define ll long long 
#define fo(i,j,n) for(register int i=j; i<=n; i++)
using namespace std;
double a,b,c,x1,y1,x2,y2,xa[6],ya[6],xb[6],yb[6];
double dist(double x1,double y1,double x2, double y2){
	return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main(){
	cin>>a>>b>>c;
	cin>>x1>>y1>>x2>>y2;
	xa[1]=x1,ya[1]=-(a*xa[1]+c)/b; // a上下 
	ya[2]=y1,xa[2]=-(b*ya[2]+c)/a; // a左右
	xb[1]=x2,yb[1]=-(a*xb[1]+c)/b; // b上下 
	yb[2]=y2,xb[2]=-(b*yb[2]+c)/a; // b上下 
	double ans = fabs(x1-x2)+fabs(y1-y2);
	fo(i,1,2)fo(j,1,2){
		ans = min(ans, fabs(xa[i]-x1)+fabs(ya[i]-y1)+fabs(xb[j]-x2)+fabs(yb[j]-y2)+dist(xa[i],ya[i],xb[j],yb[j]));
	}
	printf("%.10f\n",ans);
	return 0;
}

E题

题意:把一种质量看做一种商品。给定一些商品的重量即数列a[1],[2],a[3]…a[n],但是同学1无法区分重量和商品,但是他可以问同学2:k个商品的重量和sum有哪些。同学2会指出哪k个商品和为sum(如果存在的话)。问同学1最多能区分多少个商品,也就是说具体知道哪些商品的重量。

解法:由于同学1太傻了,所以同学2一但指出k个商品但是商品中有不同种类的,那么同学1还是无法得知哪个商品具体是哪个。
那么同学1只能询问同一种商品,k个了。
f [ i ] [ j ] f[i][j] 表示i个商品组成质量为j的组合数。
这里的组合数是这样的
如数 2 2 2 2 2 2 2 2
被算为 2 4 6 8 共4种组合 ,而不是01背包那样, 4 可以由不同的2组成,然后进行多次计算
f [ 1 ] [ 2 ] = 1 , f [ 2 ] [ 4 ] = 1 , f [ 3 ] [ 6 ] = 1 , f [ 4 ] [ 8 ] = 1 f[1][2] = 1, f[2][4] = 1, f[3][6] = 1, f[4][8] = 1
那么问题转换为:
同学1询问同一种商品(同一种质量),其质量为 a [ i ] a[i] ,数目为 k k 个,和为 s u m = k a [ i ] sum=k*a[i]
f [ k ] [ s u m ] = 1 f[k][sum] = 1 ;求最大的 k k

所以类似做 多重背包一样
但是枚举背包重量变成枚举已有的组合
f [ 0 ] [ 0 ] = 1 f[0][0] = 1 边界
f [ i + s i z e ] [ j + s i z e a [ i ] ] + = f [ i ] [ j ] f[i+size][j+size*a[i]] += f[i][j] ( s i z e size 表示选中了几个 a [ i ] a[i] )
注意:若只有2种质量,那么同学1能区别所有的商品和重量

map做法:

#include<bits/stdc++.h>
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int N, a[105],all,cnt[105];
unordered_map<int,int> f[105];
void solve(){
	f[0][0]=1;
	fo(x,1,100)if(cnt[x]){ // 访问每个物品 
		for(int i=99; i>=0; i--)for(auto p:f[i]){// 从后往前访问所有组合,保证每次使用一次有重物品,枚举已有背包
			fo(t,1,cnt[x]){// 访问物品的数目
				if(t+i>100)break;
				f[i+t][p.first+t*x] += p.second;// f[数目][值],x.first为值,x.second为totc[数目][值]的值,即组合数的多少
				//f[i][f]一定没有用过该物品,所以这次加上的数只用过一次这种物品
			}
		}
	}
	int ans = 1;
	fo(i,1,100)fo(size,1,cnt[i]){
		if(f[size][i*size]==1)ans = max(ans, all==2?N:size);
	}
	cout<<ans;
}
int main(){
	scanf("%d",&N);
	fo(i,1,N){
		scanf("%d",&a[i]);
		cnt[a[i]]++;
		if(cnt[a[i]]==1)all++;
	}
	solve();
	return 0;
}

数组做法

#include<bits/stdc++.h> 
#define ll long long 
#define fo(i,j,n) for(register int i=j; i<=n; i++)
using namespace std;
int n,a[105],cnt[105],mx,size,all;
int f[105][10505]; // f[i][j]
int zero_one(){
	f[0][0]=1;
	fo(x,1,100)if(cnt[x])
	for(int i=n;i>=0;i--)fo(j,0,mx)if(f[i][j]){ // 从后往前枚举已有背包 
		fo(t,1,cnt[x]){ // 递增序列,2,4,6,8...每个数对一个背包只使用一次 
			if(i+t>100||j+x*t>10000)break;
			f[i+t][j+x*t] += f[i][j];
		//	printf("dp[%d][%d]=%d\n",i+t,j+x*t,f[i+t][j+x*t]);
		}
	}
}
int c[101],num[101]; 
/* 错误示例
void bage(){
	int t = 0;
	fo(i,1,101)fo(j,1,cnt[i]){
		c[++t] = i*j;
		num[t] = j;
	} 
	f[0][0]=1;
	for(int i=n; i>=0; i--)fo(j,0,mx)if(f[i][j]){//这种不行只跑了一趟.... 错误示例 
		fo(x,1,n){
			if(i+num[x]>100||j+c[x]>10000)break;
				f[i+num[x]][j+c[x]] += f[i][j];
 		    printf("dp[%d][%d]=%d\n",i+1,j+c[x],f[i+num[x]][j+c[x]]);
		}
	}
}*/
void solve(){
	int ans=1;
	zero_one();
//	bage();
	fo(i,1,100){
		fo(size,1,cnt[i]){
			if(f[size][size*i]==1){// 只有一种组合,那么能区别size个数, 
				ans = max(ans, (all==2&&size==cnt[i]) ? n:size); // 若总共有两种数,那全能区别 
			}
		}
	}
	cout<<ans;
}
int main(){
	cin>>n;
	fo(i,1,n){
		scanf("%d",&a[i]),cnt[a[i]]++;
		if(cnt[a[i]]==1)all++;
		mx+=a[i];
	}
	solve();
}

猜你喜欢

转载自blog.csdn.net/weixin_39778570/article/details/84317421
今日推荐