[USACO21FEB] Minimizing Edges P
题目描述
点此看题
这道题基本不涉及任何算法,但是思维难度极高。
网上没有这道题的题解,我的思路是从官方题解那里学来的,希望能帮到你。
解法
首先讲一讲为什么我考试的时候会暴毙掉,因为我觉得最优解的构造是和原图紧密相关的,所以我在原树上搞,然后就凉了。因为这道题的提问方式是构造一个新图,没有人告诉你一定要在原图上面改,所以在适当的时候一定要抛弃原图。
首先可以求出数组
求出原图的
- 如果和
j 连边就直接让他满足条件了,那么h(j)=(x-1,y-1) - 如果
x+1<y ,同时和j_1,j_2 连边(一个点解决x 或者解决y ),则h(j_1)=(x-1,y+1),h(j_2)=(x+1,y-1) - 如果
x+1=y ,同时和j_1,j_2 连边,则h(j_1)=(x-1,y+1),h(j_2)=(x,y)
再来多解释下上面的结论,首先为什么情况这么唯一呢?因为
现在考虑怎么用这个结论来做题吧,我们把
下面就直接用贪心做了,因为第一种情况只用连一条边,而二三种情况可能会连两条边,依据这个为主要思想贪心即可。
具体实现就写一个 然后就耐性地讨论吧,时间复杂度
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
#define pii pair<int,int>
#define make make_pair
const int M = 200005;
const int inf = 1e9;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int T,n,m,k,ans,tot,f[M],a[M][2];
map<pii,int> mp,cnt;
struct edge
{
int v,next;
edge(int V=0,int N=0) : v(V) , next(N) {}
}e[2*M];
struct node
{
int x,y;
bool operator < (const node &b) const
{
if(x+y!=b.x+b.y) return x+y<b.x+b.y;
return x<b.x;
}
}b[M];
void bfs()
{
for(int i=1;i<=n;i++)
for(int j=0;j<=1;j++)
a[i][j]=inf;
queue<int> q;
q.push(1);a[1][0]=0;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(a[u][0]<inf && a[v][1]==inf)
{
a[v][1]=a[u][0]+1;
q.push(v);
}
if(a[u][1]<inf && a[v][0]==inf)
{
a[v][0]=a[u][1]+1;
q.push(v);
}
}
}
}
void solve()
{
bfs();
if(a[1][1]==1)//1有自环
{
printf("%d\n",n);
return ;
}
for(int i=1;i<=n;i++)//是二分图
if(a[i][0]==inf || a[i][1]==inf)
{
printf("%d\n",n-1);
return ;
}
for(int i=1;i<=n;i++)//把每个点分类
{
pii t=make(a[i][0],a[i][1]);
if(t.first>t.second) swap(t.first,t.second);
mp[t]++;
if(mp[t]==1)//加到数组上面排序
b[++k]=node{t.first,t.second};
}
sort(b+1,b+1+k);
for(int i=1;i<=k;i++)
{
int x=b[i].x,y=b[i].y;
pii t=make(x,y),t3=make(x+1,y-1);
pii t1=make(x-1,y-1),t2=make(x-1,y+1);
if(mp[t1]>0 && mp[t2]==0)
ans+=mp[t];//直接连上去
if(mp[t1]==0 && mp[t2]>0)
{
ans+=max(0,mp[t]-cnt[t]);//补连向j1的边
if(x+1==y)//是第三种情况
ans+=(mp[t]+1)/2;//上取整
else
{
ans+=mp[t];//连到(x+1,y-1)去
cnt[t3]+=mp[t];
}
}
if(mp[t1]>0 && mp[t2]>0)
{
if(mp[t]>cnt[t])
ans+=mp[t]-cnt[t];//连(x-1,y-1)
cnt[t]=min(cnt[t],mp[t]);//别忘了写这个
if(x+1==y)
ans+=(cnt[t]+1)/2;
else
{
ans+=cnt[t];
cnt[t3]+=cnt[t];
}
}
}
printf("%d\n",ans);
}
int main()
{
//freopen("minimizing.in","r",stdin);
//freopen("minimizing.out","w",stdout);
T=read();
while(T--)
{
mp.clear();
cnt.clear();
n=read();m=read();
tot=1;k=0;ans=0;
for(int i=1;i<=n;i++)
f[i]=0;
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
e[++tot]=edge(v,f[u]),f[u]=tot;
e[++tot]=edge(u,f[v]),f[v]=tot;
}
solve();
}
}