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
*/

 

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