牛客练习赛58
C-矩阵消除游戏
题解:看到题目首先想到的应该是按照题意贪心——每次找行或列中元素和最大的,然后元素值变为零,循环k次。但这样的贪心是错的。
比如:
3 3 2
100 100 1
10 10 1
10 10 1
贪心:(100+100+1)+(10+10+1)=222
答案:(100+10+10)+(100+10+10)=240
每次贪心后元素的值变零对之后的选择产生了影响,使最终结果产生错误。
再看n和m如此之小,所以可以采用枚举寻找最大值,在此我们使用二进制枚举枚举所有方案。
AC码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL ;
LL n,m,k;
LL a[20][20],b[20][20];
void copy(){
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
b[i][j] = a[i][j];
}
int main()
{
int ans=0;
cin>>n>>m>>k;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>a[i][j];
ans+=a[i][j];
}
}
if(k>=n||k>=m) {
cout<<ans<<endl; //全选
return 0;
}else ans = 0 ;
for(int i=0; i< 1<<n ;i++) { //二进制枚举;
int num=0,vis[20],cnt=0;
for(int j = 0 ;j<n;j++){
if((i>>j) & 1) vis[cnt++] = j;
}
if(cnt<=k) {
copy();
for(int c=0;c<cnt;c++) // 注意下标
for(int cc=0;cc<m;cc++)
num+=b[vis[c]][cc] , b[vis[c]][cc] = 0;
int sum[20]={0};
for(int c=0;c<m;c++) //注意下标
for(int cc=0;cc<n;cc++)
sum[c]+=b[cc][c];
sort(sum,sum+m);
int kk = k-cnt;
for(int c=m-1;c>=0&&kk>0;c--){
num+=sum[c];kk--;
}
ans = max(ans, num);
}
}
cout<<ans<<endl;
return 0;
}
D-迷宫
分析一波: 虽然题意描述每个格子有四个方向可以走,但是由于题目设定了方向优先级,经过分析每个格子只能往右或往下走: 因为若是往左走后, 那他的右边就是空的了,下一步他又会往右走。同理也不能往上走。所以此问题就化简成了经典的DP问题(矩阵取数)了。
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
char G[1010][1010];
int dp[1010][1010];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>G[i]+1;
memset(dp,INF,sizeof dp); //初始化dp矩阵用于判断是否达到终点且dp使用的是min;
dp[1][1] = 0; //开始置零;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(G[i][j]=='0'){
if(G[i-1][j]=='0') dp[i][j] = min(dp[i][j],dp[i-1][j]+(G[i-1][j+1]=='0')); //从上边来
if(G[i][j-1]=='0') dp[i][j] = min(dp[i][j],dp[i][j-1]); //从右边来
}
}
}
if(dp[n][m]!=INF) cout<<dp[n][m]<<endl;
else cout<<-1<<endl;
return 0;
}
E - 最大GCD
题解:题意:在 [l,r] 内寻找 一个数和x的gcd最大。vector < int > pos[N] 以因子为下标,储存元素出现的位置(该因子为元素的因子之一) ,后将位置排序,再以x的因子p从大到小判断pos[p] 中的下标是否在 [l,r] 区间内。详情请看注释。
AC码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
int n,q,t;
vector<int> pos[N];
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){ //将输入的数组预处理(拆分);
scanf("%d",&t);
for(int j=1;j<=sqrt(t);j++){ //求一个数的因子;
if(t%j==0){
pos[j].push_back(i); //pos[j] 储存某数的因子之一是j的数的下标;
if(t/j!=j) pos[t/j].push_back(i);
}
}
}
for(int i=1;i<=n;i++) sort(pos[i].begin() ,pos[i].end()); // 将下标排序,方便二分查找
while(q--){
int l,r,x;
vector<int> ans;
scanf("%d%d%d",&l,&r,&x);
for(int i=1;i<=sqrt(x);i++){
if(x%i==0){
ans.push_back(i);
if(x/i!=i) ans.push_back(x/i);
}
}
sort(ans.begin() ,ans.end() ,greater<int>()); // 将x的因子从大到小排序,一旦符合即结果
for(int i=0;i<ans.size() ;i++){
t = upper_bound(pos[ans[i]].begin() ,pos[ans[i]].end() , r) - lower_bound(pos[ans[i]].begin() ,pos[ans[i]].end() , l);
if(t){ //可用于查找某个数或区间是否存在,若存在t>0,否则 t = 0 = pos[].end() - pos[].end();
printf("%d\n",ans[i]);
break;
}
}
}
return 0;
}