缩点+染色+DFS codeforce467D
生活随笔
收集整理的這篇文章主要介紹了
缩点+染色+DFS codeforce467D
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目鏈接:https://vjudge.net/contest/219056#problem/A
推薦博客:https://blog.csdn.net/ck_boss/article/details/39429285
發現上面的這位大佬的思路和我一樣,自己代碼太多錯誤就參考了一下。
題意:
輸入n,接下來會輸入輸入n個字符串,然后輸入m,接下來會輸入m對字符串,每一對表示左邊的字符串可以變成右邊的字符串,但是右邊的不可以變成左邊的。字符串不論大小寫,現在要我們經過變換把一開始的n個字符里面的字符? ' r'或'R'(不分大小寫,你全部改成大寫或小寫)最少,如果有多個r最少,那么就換成總長度最短的,輸出r的個數和總長度。
額,網上大多數好像使用BFS寫的,但是我寫得時候因為在做圖論的題,就強行聯想到了tarjan算法,然后就不想換思路,就用tarjan加DFS寫了,代碼比較長而且比較難看,先說一下思路,有興趣的可以看代碼,雖然比較。。。
思路就是先tarjan縮點,把可以兩兩相互替換的字符串(在同一個強連通分量里面)縮點染色,同時記錄這個強連通分量里面最小的r個數和對于最短的長度。之后把縮完的點二次建圖,最后用DFS找答案。
代碼:
#include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<map> #include<stack> #include<cmath> #include<vector> #include<set> #include<cstdio> #include<string> #include<deque> using namespace std; typedef long long LL; #define eps 1e-8 #define INF 0x3f3f3f3f #define maxn 100005 struct node{int value,length; }dot[maxn],min1[maxn];//dot數字記錄每個字符串編號之后對應的值 //min1數組記錄染色之后每個強連通分量里面最符合的值 struct EDGE{int v,next; }edge[maxn],edge2[maxn];//這里要兩次建圖,一次是給字符串編號之后建圖,還有一次是縮點之后重新建圖 int a[maxn]; int head[maxn],head2[maxn]; int dfn[maxn],low[maxn],color[maxn],s[maxn]; int n,m,k,t; int cnt,time,ans,id,top,num,cnt2; map<string,int>mp;//給每個字符串編號 map<pair<int,int>,int>mmp;//重新建圖的時候防止重復的邊 bool vis[maxn]; void init() {mmp.clear();mp.clear();memset(head,-1,sizeof(head));memset(dfn,0,sizeof(dfn));memset(head2,-1,sizeof(head2));cnt=time=ans=id=top=num=0;memset(vis,false,sizeof(vis));cnt2=0;for(int i=0;i<maxn;i++){min1[i].length=min1[i].value=INF;} } void cal(string &s)//編號并計算r的個數和字符串長度 {mp[s]=++id;int value=0;for(int i=0;i<s.size();i++){if(s[i]=='r')value++;}dot[mp[s]].value=value;//dot記錄字符串s對應編號的r個數和長度 dot[mp[s]].length=s.size(); } void tarjan(int u)//縮點染色 {dfn[u]=low[u]=++time;s[top++]=u;vis[u]=true;for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].v;if(!dfn[v]){tarjan(v);low[u]=min(low[u],dfn[v]);}else if(vis[v])//必須要在棧內,因為這里不一定是一個連通圖,所以一定要判斷是否在棧內//并且一定要寫成dfn[v],不能寫成low[u] low[u]=min(low[u],dfn[v]);}if(low[u]==dfn[u]){ans++;int v;do{v=s[--top];vis[v]=false;color[v]=ans;if(dot[v].value<min1[ans].value||dot[v].value==min1[ans].value&&dot[v].length<min1[ans].length){//min1數組記錄每個強連通分量里面最優的結果 min1[ans].value=dot[v].value;min1[ans].length=dot[v].length;}}while(v!=u);} } void addedge(int u,int v) {edge[++cnt].v=v;edge[cnt].next=head[u];head[u]=cnt; } void addedge2(int u,int v) {edge2[++cnt2].v=v;edge2[cnt2].next=head2[u];head2[u]=cnt2; } void get_edge2() {for(int i=1;i<=id;i++){for(int j=head[i];j!=-1;j=edge[j].next){int v=edge[j].v;if(color[i]!=color[v]&&mmp[make_pair(color[i],color[v])]==0){addedge2(color[i],color[v]);mmp[make_pair(color[i],color[v])]=1;}//兩個強連通分量之間建一條有向圖,可能有重復邊,我們用mmp記錄去掉 }} } node bijiao(node s1,node s2) {if(s1.value<s2.value||s1.value==s2.value&&s1.length<s2.length)return s1;return s2; } void DFS(LL u) {if(vis[u])return;vis[u]=true;for(int i=head2[u];i!=-1;i=edge2[i].next){int v=edge2[i].v;if(!vis[v])DFS(v);min1[u]=bijiao(min1[u],min1[v]);}return; } string ope(string str) {for(int j=0;j<str.size();j++){if(str[j]>='A'&&str[j]<='Z')//全部變成小寫 str[j]=str[j]-'A'+'a';}return str; } int main() {while(cin>>n){init();string str;for(int i=0;i<n;i++){cin>>str;str=ope(str);if(mp[str]==0)//編號 cal(str);a[i]=mp[str];//記錄文章里面字符串的編號 }cin>>m;string s1,s2;for(int i=0;i<m;i++){cin>>s1>>s2;s1=ope(s1);s2=ope(s2);if(mp[s1]==0)cal(s1);if(mp[s2]==0)cal(s2);addedge(mp[s1],mp[s2]);//建圖,從s1的編號到s2的編號的有向邊 }for(int i=1;i<=id;i++)//求強連通分量 {if(!dfn[i])tarjan(i);}get_edge2();//縮點之后重新建圖 LL sum1=0,sum2=0;int flag[maxn];memset(vis,false,sizeof(vis));memset(flag,0,sizeof(flag));//DFS(c);//試了無數次,不知道這句為什么放在這里就錯了 ,不然可以節省很多時間 for(int i=0;i<n;i++){LL c=color[a[i]];if(!flag[c]){memset(vis,0,sizeof(vis)); DFS(c);//不知道為啥放在這里就對了?flag[c]=1;}sum1+=min1[c].value;sum2+=min1[c].length;}cout<<sum1<<' '<<sum2<<endl;}return 0; }?
轉載于:https://www.cnblogs.com/6262369sss/p/9799224.html
總結
以上是生活随笔為你收集整理的缩点+染色+DFS codeforce467D的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第四次实验 恶意代码技术
- 下一篇: python第三方库Requests的基