HDU 6302 Maximum Weighted Matching(图论/STL)

神特么图论题ORZORZORZ,fong了

大概就是通过一种操作得到一个图,求这个图的最大匹配,和取得最大的可能种数

思路倒是很容易看懂,这个代码也太emmm了吧

做法:

1.按照操作逆着dp,还原回最后只有一条边时的情况

2.注意处理每次更新的步骤(刚开始完全没想到还有可能已经存在一条相同的边,更新也一开始想错了

3.好多STL的应用,学到辽学到辽,STL真实一窍不通啊

 4.补了题我在比赛的时候也写不出这种东西吧……菜鸡咆哮

#include<iostream>
#include<string.h>
#include<assert.h> 
#include<set>
#include<map>
#include<queue>
#define go(i,a,b) for (ll i=a;i<=b;i++)
#define ll long long
#define N 100005
#define MOD 1000000007
using namespace std;
typedef pair<ll,ll>pii;

struct edge{
	ll u,v;
	ll dp[2][2],sum[2][2];
	bool operator<(const edge &e) const {
		return  v<e.v;
	}
	//set中的元素如果是结构体必须重载该运算符
	//因为set中的元素是按照大小排列的,erase比较的也是这个位置
	//这里的const如果不加的话运算符重载即失败 
};
void updata(ll &w, ll &sum, ll w1, ll sum1){
	if (w1==-1) return;
	if (w1>w) w=w1,sum=sum1;
	else if (w1==w) sum=(sum+sum1)%MOD;
	//更新数据 
}
set<edge> e[N];
int main(){
	ll T,u,v,w,ca,n,m;
	map<pii,pii>::iterator it;
	set<edge>::iterator sit;
	scanf("%lld",&T);
	for (ll ca=1;ca<=T;ca++){
		scanf("%lld%lld",&n,&m);
		map<pii,pii>mp;//读入存在map中,重复边仅保留最优,并保存最优边的个数 
		go(i,1,m){
			scanf("%lld%lld%lld",&u,&v,&w);
			if (u>v) swap(u,v);
			pii p=make_pair(u,v);
			if (!mp.count(p)) mp[p]=make_pair(w,0);
			it=mp.find(p);
			if (it->second.first<w) it->second=make_pair(w,1);
			else if (it->second.first==w) it->second.second++;
		}
		go(i,1,n) e[i].clear();//把map中的边都存到set中,好读取 
		for (it=mp.begin();it!=mp.end();it++){
			edge ne; 
			ne.u=it->first.first;
			ne.v=it->first.second;
			memset(ne.dp,-1,sizeof(ne.dp));
			memset(ne.sum,-1,sizeof(ne.sum));
			ne.dp[0][0]=0;
			ne.sum[0][0]=1;
			ne.dp[1][1]=it->second.first;
			ne.sum[1][1]=it->second.second;
			e[ne.u].insert(ne);
			swap(ne.u,ne.v);
			e[ne.u].insert(ne);
		}
		if (n==2){//只有两个点直接输出 
			it=mp.begin();
			printf("%d %d\n",it->second.first,it->second.second);
			continue;
		}
		queue<ll>q;
		go(i,1,n){//将度为2的点进队列 
			if (e[i].size()==2) q.push(i);
		}
		go(i,1,n-2){
			ll u=q.front();
			q.pop();
			edge a=*e[u].begin(); //a.v->a.u==b.u->b.v
			e[u].erase(e[u].begin());
			edge b=*e[u].begin();
			e[u].erase(e[u].begin());//删去这个点的两个出边 
			edge ne;
			ne.v=u;
			e[a.v].erase(ne);//删除到达当前点的入边 
			e[b.v].erase(ne);
			ne.u=a.v; 
			ne.v=b.v; 
			ll dp[2][2],sum[2][2];
			memset(dp,-1,sizeof(dp));memset(sum,-1,sizeof(sum));
			//两条边合并进行dp 
			go(m1,0,1)for (ll m2=0;m1+m2<=1;m2++){
				go(l,0,1)go(r,0,1){
					if (a.dp[m1][l]==-1||b.dp[m2][r]==-1) continue;
					updata(dp[l][r],sum[l][r],a.dp[m1][l]+b.dp[m2][r], (a.sum[m1][l]*b.sum[m2][r])%MOD);
				}
			}
			sit=e[ne.u].find(ne); //查找map 看有没有已经合并成l到r的边 
			bool flag=(sit!=e[ne.u].end());
			if (flag){ //若有再进行dp 
				ll dpf[2][2],sumf[2][2];
				memset(dpf,-1,sizeof(dpf));
				memset(sumf,-1,sizeof(sumf));
				//什么水平,这是什么水平
				//开始感觉这里应该是memcpy(dpf,dp,sizeof(dp)); 后来发现更新是可以更新到的 
				go(l1,0,1) go(r1,0,1){
					for (ll l2=0;l2+l1<=1;l2++)for (ll r2=0;r2+r1<=1;r2++){
						if (dp[l1][r1]==-1||sit->dp[l2][r2]==-1) continue;
						updata(dpf[l1+l2][r1+r2],sumf[l1+l2][r1+r2],dp[l1][r1]+sit->dp[l2][r2],(sum[l1][r1]*sit->sum[l2][r2])%MOD); 
					}
				}
				memcpy(dp,dpf,sizeof(dpf));
				memcpy(sum,sumf,sizeof(sumf));
				//又是什么恶魔操作memcpy 
				e[ne.u].erase(ne);
				swap(ne.u,ne.v);
				e[ne.u].erase(ne);
				swap(ne.u,ne.v);
				//双向是相同的不用再删除一次
			}
			go(l,0,1) go(r,0,1){
				ne.dp[l][r]=dp[l][r],ne.sum[l][r]=sum[l][r];
			}
			e[ne.u].insert(ne);
			swap(ne.u,ne.v);
			go(l,0,1) go(r,0,1){
				ne.dp[l][r]=dp[r][l],ne.sum[l][r]=sum[r][l];
			}
			e[ne.u].insert(ne);
			if (flag&&e[ne.u].size()==2) q.push(ne.u); //若产生了度为2的点入栈 
			if (flag&&e[ne.v].size()==2) q.push(ne.v);
		}
		ll dp=-1;
		ll sum=-1;
		edge ne=*e[q.front()].begin();
		go(l,0,1) go(r,0,1){
			updata(dp,sum,ne.dp[l][r],ne.sum[l][r]);
		} 
		printf("%lld %lld\n",dp,sum);
	}
	return 0;
} 

/*
1
4 5
1 2 1
1 3 1
1 4 1
2 3 1
3 4 1
*/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章