补题*总结题21/8/26

总结与经验

c++输入带空格字符串方式cin.getline

cin.getline(接受的字符串,接受个数,结束标志)
当第三个参数省略时,系统默认为'\0' 是‘/n’。

两点间距离新思路:(三角形两边之差小于第三边)

I - Odd Gnome(水题)

Kattis - oddgnome

Canonical Coin Systems(多重背包+贪心)

Kattis - canonical

题意:
给一组货币a[1] ~ a[n]。求买 1 ~2 × a[n] 价值的商品,用该组货币,使用贪心算法(每次挑选最大面额货币),是不是使用货币最少的解
思路:
用多重背包问题的dp求出正确解,
验证贪心是否正确
错误代码:

展开代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
/*
如果 S 是非规范的,
那么最小的反例小于两个最大面额的总和。
*/
int dp[20000001];
int main()
{
    int n;
    cin>>n;
    int a[n+1];
    int i,j;
    for(i=1; i<=n; i++)
    {
        cin>>a[i];
    }
    int max_data=a[n]+a[n];
    memset(dp,0,sizeof(dp));
    int flag=1;
    for(i=1; i<=n; i++)
    {
        for(j=max_data; j>=a[i]; j--)
        {
            if(i==1)
            {
                dp[j]=j/a[i];
            }
            else
            {
                int t=j/a[i];
                int o=dp[ j-t*a[i] ]+t;
                dp[j]=min(o,dp[j]);
                if(dp[j]!=o)//错误原因
                {
                    flag=0;
                    break;
                }
            }
        }
        if(flag==0 )
        {
            break;
        }
    }
    if(flag==1 )
        cout<< "canonical" << endl;
    else   cout<< "non-canonical" << endl;
    return 0;
}

错误原因:

      int t=j/a[i];
      int o=dp[ j-t*a[i] ]+t;
      dp[j]=min(o,dp[j]);
       if(dp[j]!=o)//错误原因
        {
          flag=0;
           break;
        }

以为这样就能模拟贪心
正确代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const int  maxn = 2e6 + 10;
/*
如果 S 是非规范的,
那么最小的反例小于两个最大面额的总和。
*/
int dp[maxn];
int tx[maxn];

int main()
{
    int n;
    cin>>n;
    int a[110];
    int i,j;
    for(i=1; i<=n; i++)
    {
        cin>>a[i];
    }




    int max_data=a[n]+a[n];

    memset(dp,INF,sizeof(dp));
    dp[0]=0;

    memset(tx,0,sizeof(tx));
    for(i=1; i<=n; i++)
    {
        for(j= a[i] ; j<=max_data; j++)
        {
            if(i==1)
            {
                tx[j]=j/a[i];
                dp[j]=j/a[i];
            }
            else
            {
            tx[j]=tx[j-a[i] ]+1;

            dp[j]=min(dp[j-a[i] ]+1,dp[j]);
            }

        }

    }

    for(i=1; i<=max_data; i++)
    {
        //cout<<dp[i]
        if(dp[i]!=tx[i])
        {
            cout<<"non-canonical"<<endl;
            return 0;
        }
    }
    cout<<"canonical"<<endl;
    return 0;
}

J - Progressive Scramble

Kattis - progressivescramble
题意:
解密加密字符串

  • 输入e表示加密,输入d则表示解码

  • 加密的规则

    • 是先给原字符一个对应的数,空格为0,字母从1开始对应直到26,一共27个合法字符。
    • 从a[0]开始,将对应位置换为字符的前缀和,然后对前缀和%27,这个数对应的字符即为加密后的字符串。
  • 解密就是逆向加密
    思路:
    模拟........
    代码:


#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int num[1005];
int sum[1005];
int main()
{
	int t;
	cin>>t;
	getchar();
	while(t--)
	{
		memset(num,0,sizeof(num));
		memset(sum,0,sizeof(sum));
		char c=getchar();
		getchar();
		string str,ans;
		getline(cin,str);
		for(int i=0;i<str.length();i++)
		{
			if(str[i]==' ')
				sum[i]=0;
			else 
				sum[i]=str[i]-'a'+1;
		}
		if(c=='e')
		{
			for(int i=1;i<str.length();i++)
				sum[i]=(sum[i]+sum[i-1])%27;
		}
		else if(c=='d')
		{
			for(int i=1;i<str.length();i++)
			{
				num[i]=num[i-1]+sum[i-1];
				while(sum[i]<num[i]) sum[i]+=27;
				sum[i]=(sum[i]-num[i])%27;
			}		
		}	
		for(int i=0;i<str.length();i++)
			if(sum[i]==0)
				ans+=' ';
			else
				ans+='a'+sum[i]-1;
		cout<<ans<<endl;
	}
	return 0;
}


G - Greeting Card

Kattis - greetingcard
题意:
给一组点,统计所有点中,任选两个点距离为2018的有多少对。
思路1:
暴力..........马上否定,一定会超时

思路2:

如果两个点距离为2018
那么只能:

  • x差2018,y相等
  • x相等,y差2018
  • x差1680,y差1118
  • x差1118,y差1680。

四种情况,问题解决
代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int N=5e5+5;
int n;
map<P,int> m;
ll sum;
int add[12][2]={{2018,0},{-2018,0},{0,2018},{0,-2018},{1118,1680},{-1118,1680},{1118,-1680},{-1118,-1680},{1680,1118},{-1680,1118},{1680,-1118},{-1680,-1118}};
int main()
{
    scanf("%d",&n);
    while(n--)
    {
        ll x,y;
        cin>>x>>y;
        sum+=m[P(x,y)];
        for(int i=0;i<12;i++) m[P(x+add[i][0],y+add[i][1])]++;
    }
    cout<<sum<<endl;
    return 0;
}




大神思路

  • 按照每个点距离原点的距离排序,如果距原点之差大于2018,则停止
    (运用的是三角形两边之差小于第三边)
    考虑到在一条直线的情况后,就是大于2018而不是大于等于2018了。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+3;
typedef unsigned long long ll ;
struct Node
{
    ll x, y;
    double len;
    friend bool operator < (Node &a,Node &b)
    {
        return a.len < b.len;
    }
}node[maxn];
ll dis(Node a, Node b)
{
    int x1 = max(a.x,b.x);
    int x2 = min(a.x,b.x);
    int y1 = max(a.y,b.y);
    int y2 = min(a.y,b.y);
    return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}
int main()
{
    int n;
    cin>>n;
    for(int i = 0; i < n ;i++)
    {
        cin>>node[i].x>>node[i].y;
        node[i].len = sqrt(node[i].x*node[i].x+node[i].y*node[i].y);
    }
    sort(node,node+n);
    int  ans = 0;
    ll base = 2018 * 2018;//精度问题,一定要比较乘方
    for(int i = 0 ; i < n; i++)
    {
         for(int j = i+1 ;j < n; j++)
         {
             if(node[j].len-node[i].len > 2018)
                break;
             if(dis(node[i],node[j]) == base )
             {
                 ans++;
             }
         }
    }
    cout<<ans<<endl;
    return 0;
}

F - GlitchBot

Kattis - glitchbot
题意:
机器人 刚开始在(0, 0),有一个目标点,
给一系列指令,但是其中会有一个指令是错误的。我们需要找出那个指令,并且改成正确的。
思路:
数据范围50.....
直接暴力:每条指令都改一下 搜索能不能走到目标点,如果可以,那么这就是答案
代码:


#include <bits/stdc++.h>
using namespace std;
int step[200];
int dx[]  = {0 , 1 , 0 , -1};//上右下左顺序固定
int dy[]  = {1 , 0 , -1 , 0};
int x_0,y_0,n;
bool judge()
{
    int direction = 0;
    int x = 0,y = 0;
    for(int  i = 0 ; i < n; i++)
    {
        if(step[i] == 0)
        {
            x = x + dx[direction];
            y = y + dy[direction];
        }
        else if(step[i] == 1) direction = (direction + 3) % 4;
        else if(step[i] == 2) direction = (direction + 1) % 4;
     }
     return (x == x_0 && y == y_0);
}
int main()
{
    cin>>x_0>>y_0>>n;
    string s;
    for(int i = 0; i < n; i++)
    {
        cin>>s;
        if(s[0] == 'F') step[i] = 0;
        else if(s[0] == 'L') step[i] = 1;
        else if(s[0] == 'R') step[i] = 2;
    }
    bool flag = 1;
    for(int i = 0 ; i < n && flag; i++)
    {
        for(int j = 0; j <= 2; j++)
        {
            step[i] = (step[i] + 1) % 3; //太精髓了吧。
            if(judge())
            {
                printf("%d ",i+1);
                if(step[i] == 0) cout<<"Forward"<<endl;
                else if(step[i] == 1) cout<<"Left"<<endl;
                else if(step[i] == 2) cout<<"Right"<<endl;
                flag = 0;
                break;
            }
        }
    }
    return 0;
}
posted @ 2021-08-26 21:05  kingwzun  阅读(44)  评论(0编辑  收藏  举报