第十二届山东省ICPC大学生程序设计竞赛(补题)

--打铁的废物

第一次打ACM省赛,心态爆炸,打铁而归,回来补题的时候发现自己就是一

A.Seventeen

1.用1到n之间的数加减乘得17即可输出 2.i + i-1 + i - 2 + i + 3 == 0 所以我们可以先写前4个然后递增即可 3.注意可以用括号 eg:(4) 因为没看清题wa在这上面了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 55;
int n;
string s[N];
int a[N];
int main()
{
   cin>>n;
   if(n < 4){
     cout<<-1<<endl;
   }else if(n==4){
     cout<<"((1+4)*3)+2"<<endl;
   }else{
      s[0] = "(3*5)-(1*2)+4+";
      s[1] = "1-2+3+4+5+6+";
      s[2] = "(1*2)+3+4-5+6+7+";
      s[3] = "(1*2)+3-4-5+6+7+8+";
      a[0] = 5, a[1] = 6, a[2] = 7, a[3] = 8;
      int t = (n - 5) % 4;
      string ss  = s[t];
      for(int i = a[t] ; i < n; i += 4)
      {
          string r = to_string(i+1) + "-" + to_string(i+2) + "-" + to_string(i+3) + "+" + to_string(i+4) + "+";
          ss += r; 
      }
      for(int i = 0 ; i < ss.length()-1 ; i++)
      {
           cout<<ss[i];
      }
      
   }
   return 0;
}

K.Coins

小范围进行dp,大范围由于A有17,19 所以全为A

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 + 10;
ll t;
int n;  
int a[N];
int b[N];
int a1[5] = {2,3,17,19};
int b1[5] = {5,7,11,13};

int main()
{
   memset(a,0x3f,sizeof a);
   memset(b,0x3f,sizeof b);
   a[0] = 0 , b[0] = 0;
   a[2] = 1 , a[3] = 1, a[17] = 1, a[19] = 1;
   b[5] = 1 , b[7] = 1, b[11] = 1, b[13] = 1;

   for(int i = 1 ; i <= 1000 ; i++)
   {
       for(int j = 0 ; j < 4 ; j++)
       {
            if(i - a1[j] > a1[0]){
                 a[i] = min(a[i] , a[i-a1[j]] + 1);
            }
            if(i - b1[j] > b1[0]){
                 b[i] = min(b[i] , b[i-b1[j]] + 1);
            }
       }
   }
   cin>>n;
   while (n--)
   {
      cin>>t;
      if(t > 1000){
           cout<<"A"<<endl;
      }else if(a[t] >= 0x3f3f3f3f && b[t] >= 0x3f3f3f3f){
           cout<<"-1"<<endl;
      }else if(a[t] < b[t]){
           cout<<"A"<<endl; 
      }else if(a[t] > b[t]){
           cout<<"B"<<endl;
      }else {
          cout<<"both"<<endl;
      }
   }
   return 0;
}

E.Subsegments

时间复杂度(nlogn) 题意找一段连续子区间其乘积为x,所以我们每次将所有数相乘,并且每一次乘x,放进map中,a = sum * x,所以我们只需要看sum什么时候等于a,那么该段区间相乘就为x a[i] = 0 时需要重新计数,因为区间不能存在0 x == 0 区间只需要存在 0 即可

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define MAXN 501000
int n;
LL a[MAXN],x;
const LL mod=998244353;
LL g[MAXN];
map<LL,LL> q;
int main()
{
    cin>>n>>x;
    q[x]++;    
    LL ans=0,sum=1;  //个数以及当前的和的数目
    for(int last=0,i=1;i<=n;i++)
    {
        cin>>a[i];
        if(x)
        {
          if(a[i]){
            a[i] %= mod;
            sum = sum * a[i] % mod; //计算sum
            g[i] = sum * x % mod;  
            ans += q[sum];    
            q[g[i]]++; 
           }else{
              //a[i]为0,重新开始
              sum = 1;
              q.clear();
              q[x]++;
           }
        }
        else
        {
            if(!a[i])last=i; // x为0  a[i]只要为零,保证最后一个是0,随便取     
            if(last)ans+=(last);
        }
    }
    cout<<ans;
    return 0;
}

H.Counting

纯纯模拟题,注意每次移动要清空

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e3+10;
typedef pair<int,int> PII;
PII dg[N];
int a[N][N];
int n,m,k,t;
string s[N];
int ans = 0;

int main() 
{
    cin>>n>>m>>k>>t;
    for(int i = 1 ; i <= k ; i++)
    {
        int x,y;
        cin>>x>>y;
        dg[i] = {x,y};
        if(a[x][y]!=0){
            ans+=a[x][y]; 
        }
        a[x][y]++;
    }
    for(int i = 1 ; i <= k ; i++)
    {
        cin>>s[i];
    }
    cout<<ans<<endl;
    ans = 0;
    for(int i = 0 ; i < t ; i++) 
    {
        ans = 0;
        
        for(int  j = 1 ; j <= k ; j++)//更新这一秒的,防止和上一秒有重复
        {
            int x = dg[j].first , y = dg[j].second;
            a[x][y] = 0;
        }

        for(int j = 1 ; j <= k ; j++)  //从第一个到第k个人遍历
        {
            int x = dg[j].first , y = dg[j].second;
            if(s[j][i] == 'R' && y < m) y++;
            else if(s[j][i] == 'L' && y > 1) y--;
            else if(s[j][i] == 'U' &&  x > 1) x--;
            else if(s[j][i] == 'D' &&  x < m) x++;
            if(a[x][y]!=0){
               ans+=a[x][y]; 
            }
            dg[j] = {x,y}; 
            a[x][y]++;  
        }
        cout<<ans<<endl;
    }
    return 0;
}

J.Football Match

第一次做计算几何,大体思路就是按照图上旗子的比例,先建造一个长度不变,但ab垂直bc的矩形,然后根据a,b之间的夹角和90度的差距,进行左右旋转 数学公式:几何问题:正弦公式,余弦公式,五角星的小角为36度,大角为252度

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e3+10;
const double PI = acos(-1.0);
struct point
{
    double x,y; 
};
int t;

point change(point a,point b,double angle){  //旋转
	double len=sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
	double jj;
	//计算两点原本存在角度
	if(abs(a.x-b.x)<=1e-6) jj=PI/2; //不存在斜率
	else jj= atan((a.y-b.y)/(a.x-b.x));//计算角度 返回与x轴的夹角角度 
	if(b.x<a.x||(abs(a.x-b.x)<=1e-6&&b.y<a.y)) jj+=PI;
	jj+=angle;
	point ans;
	ans.x=a.x+cos(jj)*len;
	ans.y=a.y+sin(jj)*len;
	return ans;
}


int main() 
{
    cin>>t;
    while (t--)
    {
        point a,b;
        cin>>a.x>>a.y>>b.x>>b.y;
        double len = sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
        point bb;
        bb.x = a.x;
        bb.y = a.y + len;
        //构建一个不倾斜的矩形,然后算旋转度数
        point c,d;
        c.x=bb.x+len/2*3;
	    c.y=bb.y;
	    d.x=a.x+len/2*3;
	    d.y=a.y;

        //计算中心点
        point o = {(a.x+c.x)/2,(a.y+c.y)/2};
        //五角星的外接圆和内切圆 
        double R = len/20*6;//五角星的比例  //外接圆半径
	    double r = R*sin(PI*18/180)/sin(PI*126/180);  //内切圆半径     大角252  小角36
	    //正弦定理  a / A = b / B   a为边 A为角

        //初始化五角星
        point e[6],j[6];
  
        double angle = PI/2; //初始90度
        for(int i = 1 ; i <= 5 ; i++)
        {
            e[i].x=o.x+cos(angle)*R; 
		    e[i].y=o.y+sin(angle)*R; 
		    angle-=PI*72/180;    //顺时针  
        }
        

        angle = PI*3/2; //初始270度
        for(int i = 1 ; i <= 5 ; i++)
        {
            j[i].x=o.x+cos(angle)*r; 
		    j[i].y=o.y+sin(angle)*r; 
		    angle-=PI*72/180;    //顺时针  
        }
        
        //设定旋转角 处理四种情况  在四个象限  以及三四象限交界处
        if(abs(a.x-b.x)>1e-6) angle=atan((a.y-b.y)/(a.x-b.x)); //a和b有斜度
	    else angle = PI/2; // 跟开始重建矩形一样
	    if(b.x<a.x||(abs(a.x-b.x)<=1e-6&&b.y<a.y)) angle+=PI; //角度发生变化

        angle -= PI/2; //旋转角
        c=change(a,c,angle);
	    d=change(a,d,angle);
    
	   for(int i=1;i<=5;i++){
		e[i]=change(a,e[i],angle); 
		j[i]=change(a,j[i],angle);
	   }

       printf("%.8f %.8f ",c.x,c.y);
	   printf("%.8f %.8f ",d.x,d.y);
	
	   printf("%.8f %.8f ",e[1].x,e[1].y);
	   printf("%.8f %.8f ",j[4].x,j[4].y);
	   printf("%.8f %.8f ",e[2].x,e[2].y);
	   printf("%.8f %.8f ",j[5].x,j[5].y);
	   printf("%.8f %.8f ",e[3].x,e[3].y);
	   printf("%.8f %.8f ",j[1].x,j[1].y);
	   printf("%.8f %.8f ",e[4].x,e[4].y);
	   printf("%.8f %.8f ",j[2].x,j[2].y);
	   printf("%.8f %.8f ",e[5].x,e[5].y);
	   printf("%.8f %.8f\n",j[3].x,j[3].y);


    }
     return 0;
}

B.Minimum Expression

dp我的痛,要学python了该 题意:一个字符串,向里面加加号,问最小值是多少 段数越大,各段的位数越接近,值越小 dp(i,j) i表示1~i个字符加进来,j表示有j段的最小数字

k中i-len-2,i-len+2 (因为n/m后,所有字段的长度不全为len,而是在len长度左右徘徊)
所以这里的i-len的范围可以开大一点

dp(i,j) = min(dp(i,j) , dp(k,j-1) + int(s(k+1,i+1))) ; 用c的话会出现精度问题

n,m=map(int,input().split())
m=m+1  # m+1 段
s=" "+input()#空格

dp=[[-1 for j in range(1010)] for i in range(1010)] #初始化
len=n//m   #向下取整
#i表示     j表示
dp[0][0]=0   # i 表示 到第几个字符  j表示该第几段
for i in range(1,n+1):  #1到n
    for j in range(1,min(i,m)+1): #i>m时最多形成m+1段   
         for k in range(i-len-2,i-len+2): #向前走多少距离  
            if k>=j-1 and k<i and dp[k][j-1]!=-1:
                if dp[i][j]==-1 or dp[i][j]>dp[k][j-1]+int(s[k+1:i+1]):   #第k+1个到i的数字  自动去前缀0
                    dp[i][j]=dp[k][j-1]+int(s[k+1:i+1])
print(dp[n][m])

明年继续加油吧

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值