选学霸
题目描述
老师想从 n n n 名学生中选 m m m 人当学霸,但有 k k k 对人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的 m m m 尽可能接近。
输入格式
第一行,三个正整数 n , m , k n,m,k n,m,k。
第 2 2 2 至第 k k k 行,每行 2 2 2 个数,表示一对实力相当的人的编号(编号为 1 , 2 , ⋯ n 1,2,\cdots n 1,2,⋯n)。
输出格式
共一行,表示既不让同学们抗议,又与原来的 m m m 尽可能接近的选出学霸的数目。
如果有两种方案与 m m m 的差的绝对值相等,选较小的一种。
样例 #1
样例输入 #1
4 3 2
1 2
3 4
样例输出 #1
2
提示
对于 100 % 100\% 100% 的数据,满足 1 ≤ n , m ≤ 2 × 1 0 4 1 \le n,m \le 2 \times 10^4 1≤n,m≤2×104。
看题 ~ 分析 ~ debug ~ AC(15分钟)
明显的并查集+0/1背包,注意答案是 0 0 0 和 n n n 的情况。
/*
A: 10min
B: 20min
C: 30min
D: 40min
*/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#include <assert.h>
#include <sstream>
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define mem(f, x) memset(f,x,sizeof(f))
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo_(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
// #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math,O3")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
template<typename T>
ostream& operator<<(ostream& os,const vector<T>&v){for(int i=0,j=0;i<v.size();i++,j++)if(j>=5){j=0;puts("");}else os<<v[i]<<" ";return os;}
template<typename T>
ostream& operator<<(ostream& os,const set<T>&v){for(auto c:v)os<<c<<" ";return os;}
template<typename T1,typename T2>
ostream& operator<<(ostream& os,const map<T1,T2>&v){for(auto c:v)os<<c.first<<" "<<c.second<<endl;return os;}
template<typename T>inline void rd(T &a) {
char c = getchar(); T x = 0, f = 1; while (!isdigit(c)) {if (c == '-')f = -1; c = getchar();}
while (isdigit(c)) {x = (x << 1) + (x << 3) + c - '0'; c = getchar();} a = f * x;
}
typedef pair<long long ,long long >PII;
typedef pair<long,long>PLL;
typedef long long ll;
typedef unsigned long long ull;
const int N=2e5+10,M=1e9+7;
// 重写max函数
template<typename T>
inline T Max(T a,T b){
return a>b?a:b;
}
int n,m,k;
int fa[20010],sz[20010];
int f[20010];
struct good{
int v;
};
vector<good>g;
int find(int u){
if(fa[u]==u)return u;
return fa[u]=find(fa[u]);
}
void merge(int a,int b){
int x=find(a),y=find(b);
if(x==y)return;
if(sz[x]<sz[y])swap(x,y);
fa[x]=y;
sz[y]+=sz[x];
}
void solve(){
cin>>n>>m>>k;
fo(i,1,n)fa[i]=i,sz[i]=1;
fo(i,1,k){
int a,b;cin>>a>>b;
merge(a,b);
}
f[0] = 1;
fo(i,1,n){
if(fa[i] == i){
good tmp;
tmp.v=sz[i];
g.pb(tmp);
}
}
for(auto c:g){
int v = c.v;
for(int j=n;j>=v;j--){
f[j] |= f[j-v];
}
}
int ans = m;
while(!f[ans]){
ans--;
}
int res = m+1;
while(!f[res] && res+1<=n){
res++;
}
if(abs(ans-m)>abs(res-m))ans=res;
else if(abs(ans-m) == abs(res-m)){
if(ans>res)ans=res;
}
cout<<ans<<endl;
}
int main(){
solve();
return 0;
}
最后我枚举答案写的超级丑,学到了更好看的方法
int ans=0,dist=0x3f3f3f3f;
for(int i=0;i<=n;i++){
if(f[i] && abs(m-i)<dist){
dist = abs(m-i);
ans = i;
}
}