程序设计思维与实践 CSP-M4

在这里插入图片描述
这道题卡了stl和cin,把输入的数换成字符串计算次数就行了。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int main(){
 int n,k;
 cin>>n>>k;
 char ss[20];
 int ans=0;
 bool num[10];
 while(n--){
  memset(num,0,sizeof(num));
  int count=0;
  scanf("%s",ss);
  for(int i=0;i<strlen(ss);i++){
   int ind=ss[i]-'0';
   num[ind]=1;
  }
  for(int i=0;i<10;i++){
   if(num[i]==1)
    count++;
  }
  if(count<k)
   ans++;
 }
 cout<<ans<<endl;
 return 0;
}

在这里插入图片描述
当初做用的算法是类似于求最长路的算法,先从一个点开始求距离最远的点,再从这个距离最远的点求另一个最远的点,再遍历每一个点,取到两个最远点的距离中的较大值,这个值最小的点就是保护罩中心。
但是爆零了,这个算法会出现意外情况,画个图就明白了。
正确算法:暴力求出到其他点的最大距离最小的那个点。
见代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include <bits/stdc++.h>
using namespace std;
struct location{
 double x;
 double y;
 bool operator<(const location &t)const{
  if(x!=t.x){
   return x<t.x;
  }
  else{
   return y<t.y;
  }
 }
}loc[1010];
double dist(location a,location b){
 return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
int main(){
 int n;
 cin>>n;
 for(int i=0;i<n;i++){
  cin>>loc[i].x>>loc[i].y;
 }
 sort(loc,loc+n);
 int fa,fb;
 double dis=DBL_MAX;
 int ansind=0;
 for(int i=0;i<n;i++){
  double tmp=0;
  for(int j=0;j<n;j++){
   if(i!=j){
    tmp=max(tmp,dist(loc[i],loc[j]));
   }
  }
  if(dis>tmp){
   dis=tmp;
   ansind=i;
  }
 }
 printf("%.2lf %.2lf\n%.2lf",loc[ansind].x,loc[ansind].y,dis);
 return 0;
}

在这里插入图片描述

这道题我竟然没有看出来是区间DP,想暴力求所有二叉搜索树的情况却败给。直到看了某大佬的博客才幡然醒悟,狂扇自己嘴巴。

算法:
首先求出所有的gcd值,存在gcdarr[][]二维数组中。gcdarr[i][j]表示第i个数和第j个数的gcd值。
开bool dp[][][]三维数组,dp[i][j][0]表示以ai为根,aj~ai-1为左子树的树是否成立,
dp[i][j][1]即ai为根,ai+1~aj为右子树的树是否成立。
如果成立置为1,不成立置为0。根据二叉搜索树的性质,如果i为根,j为左子树或者右子树中最小或者最大的那个树,j到i-1或者i+1到j都在这棵树里,我们所需要做的就是往左右扩展,如果往左扩展,也就是这棵树即将成为扩展到的数的右子树。
如果往右扩展,也就是这棵树即将成为扩展到的数的左子树。
状态转移方程
dp[j+1][i][0] |= gcdarr[j+1][k];
dp[i-1][j][1] |= gcdarr[i-1][k];
解释:
[i,j]成立,且[i,k],[k,j]都成立,也就是k是根,i 到k-1是左子树,k+1到j是右子树。
往右扩展时,j+1是根,原来的树成了j+1的左子树。
往左扩展,i-1是根,原来的树成了i-1的右子树。

好了,说了这么多,再结合代码,相信读者肯定能看明白。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=710;
int n,a[N];
bool gcdarr[N][N],dp[N][N][2];
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
int main(){
 int t;
 scanf("%d",&t);
 while(t--){
  scanf("%d",&n);
  memset(dp,0,sizeof(dp));
  memset(gcdarr,0,sizeof(gcdarr));
  for(int i=1;i<=n;i++){
   scanf("%d",&a[i]);
  }
  for(int i=1;i<=n;i++){
   for(int j=1;j<=n;j++){
    if(i==j) 
     gcdarr[i][j]=0;
    else
     gcdarr[i][j]=(gcd(a[i],a[j])>1)?1:0;
   }
  }
  for(int i=1;i<=n;i++)
   dp[i][i][0]=dp[i][i][1]=1;
  for(int j=1;j<=n;j++){
   for(int i=j;i>=1;i--){
    for(int k=i;k<=j;k++){
     if(dp[k][i][0]&&dp[k][j][1]){
      //j+1或i-1能不能插入树里  
      //状态转移方程
      dp[j+1][i][0]|=gcdarr[j+1][k];
      dp[i-1][j][1]|=gcdarr[i-1][k];
     }
    }
   }
  }
  bool flag=false;
  for(int i=1;i<=n;i++){
   if(dp[i][1][0]&&dp[i][n][1]){
    printf("Yes\n");
    flag=true;
    break;
   }
  }
  if(flag==false)
   printf("No\n");
 }
 return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值