T1
题面
虚假的一般图最小匹配
时间限制:1秒 内存限制:256M
题目描述
给定 n 个点的完全图,第 i 个的点权为 ai 。点 i 和 j 之间的边的边权为∣ai−aj∣。
小可想求出这张图的最小 m 匹配,最小 m 匹配的定义如下:
从图中选出 2*m 个点和 m 条边,每一条选中的边都恰好连接两个选中的点,每一个选中的点都恰好被一条选中的边相连。
匹配的权值定义为所有选中的边的权值和。
最小 m 匹配是找出如上描述中,匹配权值最小值。
输入描述
第一行:输入两个整数n,m,表述如题。
第二行:输入n个数字,表示每个点的点权。
输出描述
最小 m 匹配的值。
输入样例1
4 1
2 4 7 3
输出样例1
1
输入样例2
8 3
9 2 3 12 11 7 6 5
输出样例2
3
数据描述
20%的数据:1≤n≤10
40%的数据:1≤n≤100
另有10%的数据:1≤m≤50
另有20%的数据:1≤ai≤5000
对于100%的数据:1≤2∗m≤n≤5000,1≤ai≤10e9
A
初次思路
将原数组排序,两两分割为相邻的实数对,取差值,将差值再次排序,将该序列中前m个差值相加得到答案
为啥不对?
排序后的数组不仅仅可以相邻两两分割,也可以胡乱分,考虑不全面
复盘思路
先将数组排序,随后
DP,启动
dp[ i ][ j ]:在前i个数中选j对数的最小代价
对于每一个i、j,有选/不选两种状态
👇少选了一对的状态是j-1
选:代价=dp[ i - 2 ][ j - 1 ]+(a[ i ]-a[ i - 1 ])👈增加上的权值
👆因为i代表的是对的数量,所以应该-2
不选:代价=dp[ i ][ j - 1 ]👈少选了一对的状态是j-1
以上两种取min即可
code
#include<bits/stdc++.h>
using namespace std;
long long a[5005],dp[5005][2505];
int main(){
//freopen("match.in","r",stdin);
//freopen("match.out","w",stdout);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
sort(a+1,a+1+n);
for(int i=1;i<=m;i++){
dp[0][i]=dp[1][i]=(long long)0x3f3f3f3f;
}
for(int i=2;i<=n;i++){
dp[i][0]=0;
for(int j=1;j<=m;j++){
dp[i][j]=dp[i-1][j];
dp[i][j]=min(dp[i][j],dp[i-2][j-1]+abs(a[i]-a[i-1]));
}
}
cout<<dp[n][m];
//fclose(stdin);
//fclose(stdout);
return 0;
}
T2
题面
序列删除与填充
时间限制:2秒 内存限制:256M
题目描述
给定一个长度为 n 的正整数序列,其中有些位置为空,另一些位置则已经填好了数字。
你需要删去序列中的一个位置(可以是空的位置,也可以不是),然后在剩余的位置中填入数字,满足:
1.所有的数字均为 [1,n] 的数字,且两两不同,数据保证已经填充的数字满足该条件。
2.该序列的字典序最小。
输入描述
第一行:输入一个正整数T,表示多组输入的组数。
对于每组测试样例:
第一行:输入一个正整数 n,表示序列长度。
第二行:输入n个整数,代码该序列a,如果 ai=0,表示这个位置为空,尚未填入数字。
输出描述
对于每组样例,输出一行n-1个数字,表示经过删除和填入之和的最小字典序的序列。
输入样例
2
3
2 0 3
4
4 0 0 2
输出样例
1 3
1 3 2
输入样例
2
7
3 0 4 0 1 0 2
6
1 3 4 5 2 6
输出样例
3 1 4 5 6 2
1 3 4 2 6
数据描述
对于20%的数据:2≤n≤8,1≤T≤5
对于30%的数据:2≤n≤100,1≤T≤5
对于50%的数据:2≤n≤2000,1≤T≤10
对于另外5%的数据:不存在空的位置
对于另外15%的数据:空的位置数量≤50≤50
对于100%的数据:2≤n≤2∗105,2≤∑n≤106,1≤T≤100
初次思路
对于(对于另外5%的数据:不存在空的位置)的部分,用
剩下的枚举,抠20分走人(其实没抠到)
复盘思路
比(su)较(per)细节的贪心,考虑先删后填
删:
先记录每个数的位置及后缀最小值,备用
将未出现的数放入优先队列中,备用
每遇到一个为零的数,如果后面有比优先队列中待填的数更小的,就在他后面删一个数填到这里
每遇到一个数的下一个数不为零的,是拐点则删
每遇到一个数的下一个数为零的,该数比优先队列中待填的数更大则删(本质上也是拐点)
填:
将剩余优先队列中待填的数填入即可
code
#include<bits/stdc++.h>
#define MAXN 100
#define ull unsigned long long int
#define ll long long int
using namespace std;
int t,n,a[1000005],pos[1000005],vis[100005],tmp[105],ans[105],fl;
int main(){
scanf("%d",&t);
int T=t;
while(t--){
scanf("%d",&n);
memset(vis,0,sizeof vis);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
vis[a[i]]++;
pos[a[i]]=i;
}
for(int i=1;i<=n;i++){
if(vis[i]==0){
qu.push(i);
}
}
bmin[n+1]=inf;
for(int i=n;i>=1;i--){
bmin[i]=//后缀最小值
}
int P=n;
for(int i=1;i<n;i++){
if(!a[i]){
if(bmin[i]>qu.top()){
a[i]=qu.top();
qu.pop();
}
else{
a[i]=bmin[i],P=pos[a[i]];
break;
}
}
if(a[i+1]){
if(a[i]>a[i+1]){
P=i;
qu.push(a[i]);
break;
}
}
else{
if(a[i]>qu.top()){
P=i;
qu.push(a[i]);
break;
}
}
}
for(int i=1;i<=n;i++){
if(a[i]||i==P){
continue;
}
a[i]=qu.top();
qu.pop();
}
//cout
printf("\n");
}
return 0;
}
T3
:(
T4
:)
骗到15分