7-1 懂的都懂 (20 分)
题意:
给出n个数a1,a2,a3…an。(n≤50)
给出m个数,判断这个数是否为 从n个数中抽4个,这4个数的平均数?
思路:
n很小,m很大。如果依次判断这m个数的话,每次遍历找n个数中的4个,复杂度很高。
所以可以将这n个数中任意4个数的所有平均数map标记下来,然后O(1)判断这m个数是否出现过就行了。
只要标记4个数的和,判断4*x是否出现过。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, a[N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
for(int k=j+1;k<=n;k++){
for(int l=k+1;l<=n;l++){
mp[a[i]+a[j]+a[k]+a[l]]=1;
}
}
}
}
for(int i=1;i<=m;i++)
{
int k,flag=0;
cin>>k;
for(int j=1;j<=k;j++)
{
int x;cin>>x;
if(!mp[4*x]) flag=1;
}
if(flag) cout<<"No\n";
else cout<<"Yes\n";
}
return 0;
}
7-2 芬兰木棋 (25 分)
题意:
给出n个二维坐标,每个坐标有一个权值。
一个人可以瞄准一个角度,可以选择下面两种操作:
- 打中距离最近的一个点,获得这个点的所有权值wi (1 ≤ w ≤ 1000)。
- 打中距离最近的 k 个点,获得 k 个权值。
问最多能够得到多少权值,在这种情况下,最少操作多少次?
思路:
最多能够得到的权值肯定是所有点的权值之和,那么能够得到所有点的权值最少的操作数为多少呢?
为了能够一次操作打倒多个目标,并且没有权值损失,那么这个操作打倒的只能为权值都为1的目标点。
如何能按距离从小到大遍历一个角度上的所有点呢?如何表示角度呢?
通常都是用斜率来表示,y/x
,但是还需要将第一象限和第三象限、第二象限和第四象限分别标记。还要判断 x 为0的情况。
题解介绍了另一个表示角度(斜率)的方法~
将分子分母化为最简分数,然后将分子分母分别存到pair中,用map映射!
为了区分第一象限和第二象限,分子 x,分母 y 要除以
g
c
d
(
∣
x
∣
,
∣
y
∣
)
gcd(|x|,|y|)
gcd(∣x∣,∣y∣),加绝对值。
但是要把这个角度上的所有点存下来,那么这个 map 就要映射为一个数组,我们可以将 vector 嵌入为 map 的第二元素。这个点要存下两个元素,距离和权值。(因为后面要按距离将所有的坐标排序)所以可以将 pair 嵌入到 vector 中。
所以 map 的形式就为:
map< pair<int,int>, vector<pair<int,int>> > mp;
Code:
map <PII,vector<PII>> mp;
const int N = 200010, mod = 1e9+7;
int T, n, m, a[N];
bool cmp(PII a,PII b){
return a.first<b.first;
}
int main(){
cin>>n;
ll sum=0;
for(int i=1;i<=n;i++){
int x,y,z;cin>>x>>y>>z;
int t=__gcd(abs(x),abs(y));
mp[{x/t,y/t}].push_back({(ll)x*x+(ll)y*y,z});
sum+=z;
}
ll cnt=0;
for(auto i=mp.begin();i!=mp.end();i++) //遍历所有的角度(斜率)
{
auto v=i->second;
sort(v.begin(),v.end()); //将这个角度上的点按距离排序
for(int j=0;j<v.size();j++)
{
if(j&&v[j].second==v[j-1].second&&v[j].second==1) continue;
cnt++;
}
}
cout<<sum<<" "<<cnt;
return 0;
}
技巧性很强的一道题,让我认识到了map还能映射为vector!
7-3 打怪升级 (25 分)
思路:
floyd跑最短路,暴力找出起点。
单源最短路:dijkstra跑最短路,在满足到一个点距离最短的前提下,还要保证价值就总和最大。记录路径。
更新相邻点时:
- 相邻点最短距离可更新,那么更新此点距离,更新价值,放入队列。
- 相邻点距离为最短,但是价值可更新,那么更新价值,不放入队列。
Code:
const int N = 1010,M = 2000010;
int T, n, m, a[N];
int e[M],ne[M],h[N],w[M],val[M],idx;
int dist[N],f[N],sum[N];
int pre[N];
int d[N][N];
void add(int x,int y,int tw,int tval){
e[idx]=y,w[idx]=tw,val[idx]=tval,ne[idx]=h[x],h[x]=idx++;
}
int dij(int st)
{
mem(dist,0x3f);
for(int i=1;i<=n;i++) pre[i]=i;
priority_queue<PII,vector<PII>,greater<PII> > que;
que.push({0,st});
dist[st]=0;
while(que.size())
{
int x=que.top().second;
que.pop();
if(f[x]) continue;
f[x]=1;
for(int i=h[x];i!=-1;i=ne[i])
{
int tx=e[i];
if(dist[tx]>dist[x]+w[i]){
dist[tx]=dist[x]+w[i];
sum[tx]=sum[x]+val[i];
pre[tx]=x;
que.push({dist[tx],tx});
}
if(dist[tx]==dist[x]+w[i]){
if(sum[tx]<sum[x]+val[i]){
sum[tx]=sum[x]+val[i];
pre[tx]=x;
}
}
}
}
int maxa=-1;
for(int i=1;i<=n;i++){
if(dist[i]>maxa) maxa=dist[i];
}
return maxa;
}
void pree()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j) d[i][j]=0x3f3f3f3f;
}
void floyd()
{
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(d[i][j]>d[i][k]+d[k][j]) d[i][j]=d[i][k]+d[k][j];
}
}
}
}
int main(){
Ios;
cin>>n>>m;
mem(h,-1);
pree();
while(m--)
{
int x,y,w,val;
cin>>x>>y>>w>>val;
add(x,y,w,val);
add(y,x,w,val);
d[x][y]=d[y][x]=w;
}
int k;cin>>k;
for(int i=1;i<=k;i++) cin>>a[i];
floyd();
int st,mina=1e9;
for(int i=1;i<=n;i++){
int t=-1;
for(int j=1;j<=n;j++){
if(d[i][j]>t) t=d[i][j];
}
if(t<mina) mina=t,st=i;
}
cout<<st<<endl;
dij(st);
for(int i=1;i<=k;i++){
int x=a[i];
cout<<st;
while(x!=pre[x]){
stk.push(x);
x=pre[x];
}
while(stk.size()) cout<<"->"<<stk.top(),stk.pop();
cout<<endl;
cout<<dist[a[i]]<<" "<<sum[a[i]]<<endl;
}
return 0;
}