hdu 2242(边双连通分量)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2242
思路:Tarjan求边双连通分量,进行缩点后成树,然后就是树型dp,求一下最小差值就行了。
ps:注意点:由于双向建边,DP搜索时要记录父亲,只能往下搜,不能搜该节点的父亲。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stack> 6 #include<vector> 7 using namespace std; 8 #define MAXN 10000+10 9 #define inf 1<<30 10 stack<int>S; 11 vector<int>vet[MAXN]; 12 vector<int>map[MAXN]; 13 int n,m,cnt,_count,sum,MIN; 14 int dfn[MAXN],low[MAXN]; 15 bool mark[MAXN]; 16 int color[MAXN]; 17 int dp[MAXN]; 18 int value[MAXN]; 19 20 //tarjan算法找边双联通分量并进行缩点 21 void Tarjan(int u,int father){ 22 int flag=0; 23 dfn[u]=low[u]=++cnt; 24 mark[u]=true; 25 S.push(u); 26 for(int i=0;i<vet[u].size();i++){ 27 int v=vet[u][i]; 28 if(v==father&&!flag){ flag=1;continue; }//考虑重边情况,重要 29 if(!mark[v])Tarjan(v,u); 30 low[u]=min(low[u],low[v]); 31 } 32 if(low[u]==dfn[u]){ 33 _count++; 34 int v; 35 do{ 36 v=S.top(); 37 S.pop(); 38 color[v]=_count;//染色 39 dp[_count]+=value[v];//求缩点后的价值 40 }while(u!=v); 41 } 42 } 43 44 //树型dp求最小差值 45 int dfs(int u,int father){ 46 int ans=dp[u]; 47 for(int i=0;i<map[u].size();i++){ 48 int v=map[u][i]; 49 if(v==father)continue; 50 ans+=dfs(v,u); 51 } 52 MIN=min(MIN,abs((sum-ans)-ans)); 53 return ans; 54 } 55 56 57 58 int main(){ 59 // freopen("1.txt","r",stdin); 60 int u,v; 61 while(~scanf("%d%d",&n,&m)){ 62 sum=cnt=_count=0;MIN=inf; 63 for(int i=0;i<=n;i++){ vet[i].clear();map[i].clear(); } 64 for(int i=0;i<n;i++){ scanf("%d",&value[i]);sum+=value[i]; } 65 while(m--){ 66 scanf("%d%d",&u,&v); 67 vet[u].push_back(v); 68 vet[v].push_back(u); 69 } 70 memset(color,0,sizeof(color)); 71 memset(dfn,0,sizeof(dfn)); 72 memset(low,0,sizeof(low)); 73 memset(mark,false,sizeof(mark)); 74 memset(dp,0,sizeof(dp)); 75 Tarjan(0,0); 76 if(_count==1){ puts("impossible");continue; } 77 for(int i=0;i<n;i++){ 78 for(int j=0;j<vet[i].size();j++){ 79 if(color[i]!=color[vet[i][j]]){ 80 map[color[i]].push_back(color[vet[i][j]]); 81 } 82 } 83 } 84 dfs(1,0); 85 printf("%d\n",MIN); 86 } 87 return 0; 88 }