补题*总结题21/8/26
总结与经验
c++输入带空格字符串方式cin.getline
cin.getline(接受的字符串,接受个数,结束标志)
当第三个参数省略时,系统默认为'\0' 是‘/n’。
两点间距离新思路:(三角形两边之差小于第三边)
I - Odd Gnome(水题)
Canonical Coin Systems(多重背包+贪心)
题意:
给一组货币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;
}