Wannafly挑战赛26
A
这题就是一个暴力 才1000组 我都不知道自己在想啥 真的是 状态不好 越做越菜。。。。。
暴力解法没啥好说的
但是 看了别人的代码 都用的C++ 刻意去把自己的代码修改了修改 发个博客
主要就是想练习并且记录下STL的用法 C++不熟练导致的就是 STL真的是差啊~~~
以后写题 不影响运行时间的情况下 能用STL就用STL 有些容器确实比结构体方便多了啊
ac代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
const int INF=0x3f3f3f3f;
vector<pair<ll,ll> >v;
int n;
ll x,y;
int main(){
scanf("%d",&n);
while(n--){
scanf("%lld%lld",&x,&y);
v.push_back(pair<ll,ll>(x,y));
}
for(int i=0;i<v.size();i++){
x=v[i].first;
y=v[i].second;
ll flag=-1;
for(int j=0;j<v.size();j++){
if(i==j)continue;
ll xx=v[j].first;
ll yy=v[j].second;
ll d=(x-xx)*(x-xx)+(y-yy)*(y-yy);
if(flag==-1)flag=d;
if(d!=flag)break;
if(j==v.size()-1){
printf("%d\n",i+1);return 0;
}
}
}
printf("-1\n");
return 0;
}
B
思路:
比赛时候 自己想错了 错误的思路这里就不说了 省的在这写一遍还让自己记住了那就傻逼了
赛后看了别人的思路 才会写这个题
首先 根据题意 我们知道 我们选择任何一行 对手一定会选择这一行中的最大值拿走
那么显然 我们应该统计出来矩阵每一行的最大值 然后在最大值里面每次选择最小的那一行
这里有一个性质 当我们选择一行时 一定会一直选择这一行 直到对手把这一行全部拿完才会 换别的行
证明一下
假设现在情况如下
三行,每行都从小到大排好序了 | 每行最大值 | ||
x | y | z | z |
a | b | c | c |
1 | 2 | 3 | 3 |
假设 z c 3中 z < c < 3 那我们会选择最小的 z 那么选完了现在三行的最大值是 y c 3
而 y < z z < c < 3 所以 y一定是最小的 我们会选择 y 同理我们会选择x
由此证明 选择到一行的时候 一定会把这行选择完
那么 问题就转化为了
每次我们选择一行 如果 k 的大小 足够我们拿完这行 我们就会拿完 k不够 我们就会拿走最大的 k 个
那么 想让对手拿的最少 我们将每行的和统计出来 排个序 其中最小的 k/m 个就是对手要拿走的(k%m==0)
当然 如果 k%m!=0 也就是说 我们拿走最小的 k / m 行后 还要在一行里拿走 k%m个元素
你可能会想 这好办 拿走最小的 k/m 行 然后再在剩下的最下的行里面拿走 k%m个最大的元素就ok了
但是 比如 数据
2 3 4 (n,m,k)
5 6 7 ------和是18
20 1 1 ------和是22
显然 你会拿走和最小的行 5 6 7 然后拿走20 得到了38 而最优解是27
所以 这个余数行的选择 不能以和最小为评判标准
要枚举 并且枚举范围是 1-n所有行
枚举每一行做为余数行 从中拿走 m%k 个最大的数 然后再加上和最小的m/k行
枚举中 最小的那个答案 就是最优解
ps: long long 最大值设置成 8e18 也不会爆 刚开始设置成 1e18 太小了~~~
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#include<vector>
using namespace std;
const int maxn=1e3+7;
const int INF=0x3f3f3f3f;
const long long INFF=8e18;
typedef long long ll;
pair<long long,int> b[maxn];
ll a[maxn][maxn];
ll sum;
int n,m,k;
bool cmp(int x,int y){return x>y;}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++){
ll maxx=0;//统计每一行的和
for(int j=1;j<=m;j++){
scanf("%lld",&a[i][j]);maxx+=a[i][j];
}
sort(a[i]+1,a[i]+m+1,cmp);//每一行从大到小排序
b[i].first=maxx;//记录每一行的和
b[i].second=i;//and 每一个和的下标
}
sort(b+1,b+n+1);//对n行的和 从小到大排序
if(k%m==0){//能取走整数行的 直接和最小的k/m行加起来
for(int i=1;i<=k/m;i++){
sum+=b[i].first;
}
}
else{//存在余数行的情况
for(int i=1;i<=k/m;i++)sum+=b[i].first;
ll cnt=0,ans=INFF;//8e18不会爆ll
for(int i=1;i<=n;i++){//枚举余数行
cnt=sum;
if(i<=k/m){//维护cnt永远是除了余数行 剩下的和最小的m/k行之和
cnt-=b[i].first;cnt+=b[k/m+1].first;
}
for(int j=1;j<=(k%m);j++)cnt+=a[b[i].second][j];
ans=min(ans,cnt);//取最小的就是最优解
}
sum=ans;
}
printf("%lld\n",sum);
return 0;
}