喵哈哈村的魔法大师 【SCC+缩点+floyd求传递闭包+最大匹配+01 背包】
2017-05-30 23:55
483 查看
题目链接
╳灬兲笙疯癫°月是月大叔的ID,他是一个掌握着429种魔法的魔法大师,最擅长的技能就是搞事,今天他又要开始搞事了。
现在有一个由n个点,m条边组成的有向图,每个点上面都有一个魔法家族。
现在月大叔为了通知别人参加他的魔法大会,于是他就要拜访所有的家族。
由于是有向图,所以可能存在过去,就无法回来的情况,但是没有关系。
月大叔可以创造传送卷轴,传送卷轴可以传送到任意一个点。
第i张传送卷轴的造价是
a
i
ai元,可以使用
c
i
ci次,当然每个卷轴只能造一次。
现在问你
4000
月大叔最少需要多少钱,就可以拜访所有的人了。
注意一开始月大叔就得使用一次传送魔法噢。
INPUT
输入第一行包含一个正整数
t
(
1
≤
t
≤
100
)
t(1≤t≤100) ,表示有t组数据
对于每组数据:
第一行三个整数n,m,k。表示有
n
(
1
≤
n
≤
500
)
n(1≤n≤500)个点,
m
(
1
≤
m
≤
n
∗
(
n
−
1
)
/
2
)
m(1≤m≤n∗(n−1)/2)条边,
k
(
1
≤
k
≤
500
)
k(1≤k≤500)种魔法卷轴
接下来m行,每行两个整数u,v,表示u到v之间有一条有向边,可能存在自环、重边的情况。
接下来k行,每行两个整数
a
i
(
1
≤
a
i
≤
500
)
ai(1≤ai≤500),
c
i
(
1
≤
c
i
≤
500
)
ci(1≤ci≤500),分别表示卷轴的造价和使用次数。
OUTPUT
对于每组测试数据的询问,输出有多少对即可。
对于每组测试数据的询问,输出最少花多少钱即可
如果无解的话,请输出”-1”
SAMPLE INPUT
1
5 5 3
5 2
2 5
1 4
1 1
2 1
4 2
4 1
4 3
SAMPLE OUTPUT
4
SOLUTION
玲珑杯”ACM比赛 Round #15
错的思路,scc+缩点+统计入度为0的个数+01背包,其实是不对的,对与DAG图的理解还是太差了(主要还是太混乱了 ),才导致的。 仔细看下面的,就会明白 为什么会错了。
前提知识 务必理解
1scc+缩点 【模板】
2最小路径覆盖【例题解释】 // 最好 和本题 对比 ,搞清楚什么时候是 可相交的最小路径覆盖,什么时候是 不可相交的最小路径覆盖。 一般图的最小路径覆盖求的是不相交的(题目上会有说明,不会走重复点之类的提示)。。而这道题不是,没有强调不能够重复,所以我们要用floyd求一下才可以。,
3floyd 求传递闭包 例题
floyd 求传递闭包作用 ,由于我也是第一次接触这个(虽然离散课刚结课Σ( ° △ °|||)︴) 。就找了个感觉还算典型的例题来理解下;
对DAG 图的理解
1建议 将这道题和下面这道题仔细对比一下
例题点我
DAG 图中,从一个入度为0 的点 确实是可以到达 与其相连的所有点,但是这道题目是,拜访一遍就是确确实实的走一遍(选择一个方向的话,走到头就走不动了,不能够返回原入度为0的点),而上面的例题是 从入度为0的点 可以向好多方向打电话,相当于走了好几条路。 所以这是不同的。。
所以就引出了 最小路径覆盖(最大匹配求解)这个概念。==》走 互不相交的路径,问最少要走几条才可以走完全部的点。
是不是和这道题已经很接近了,但还不是,这道题题的意思就是 可以走重复的路径,即是要 求可以相交的型的 最小路径覆盖(所以这里要先用Floyd求传递闭包,然后在求解 最大匹配)。最后再来一下 01 背包求一下就行。
先来个dalao的代码
我的代码
╳灬兲笙疯癫°月是月大叔的ID,他是一个掌握着429种魔法的魔法大师,最擅长的技能就是搞事,今天他又要开始搞事了。
现在有一个由n个点,m条边组成的有向图,每个点上面都有一个魔法家族。
现在月大叔为了通知别人参加他的魔法大会,于是他就要拜访所有的家族。
由于是有向图,所以可能存在过去,就无法回来的情况,但是没有关系。
月大叔可以创造传送卷轴,传送卷轴可以传送到任意一个点。
第i张传送卷轴的造价是
a
i
ai元,可以使用
c
i
ci次,当然每个卷轴只能造一次。
现在问你
4000
月大叔最少需要多少钱,就可以拜访所有的人了。
注意一开始月大叔就得使用一次传送魔法噢。
INPUT
输入第一行包含一个正整数
t
(
1
≤
t
≤
100
)
t(1≤t≤100) ,表示有t组数据
对于每组数据:
第一行三个整数n,m,k。表示有
n
(
1
≤
n
≤
500
)
n(1≤n≤500)个点,
m
(
1
≤
m
≤
n
∗
(
n
−
1
)
/
2
)
m(1≤m≤n∗(n−1)/2)条边,
k
(
1
≤
k
≤
500
)
k(1≤k≤500)种魔法卷轴
接下来m行,每行两个整数u,v,表示u到v之间有一条有向边,可能存在自环、重边的情况。
接下来k行,每行两个整数
a
i
(
1
≤
a
i
≤
500
)
ai(1≤ai≤500),
c
i
(
1
≤
c
i
≤
500
)
ci(1≤ci≤500),分别表示卷轴的造价和使用次数。
OUTPUT
对于每组测试数据的询问,输出有多少对即可。
对于每组测试数据的询问,输出最少花多少钱即可
如果无解的话,请输出”-1”
SAMPLE INPUT
1
5 5 3
5 2
2 5
1 4
1 1
2 1
4 2
4 1
4 3
SAMPLE OUTPUT
4
SOLUTION
玲珑杯”ACM比赛 Round #15
错的思路,scc+缩点+统计入度为0的个数+01背包,其实是不对的,对与DAG图的理解还是太差了(主要还是太混乱了 ),才导致的。 仔细看下面的,就会明白 为什么会错了。
前提知识 务必理解
1scc+缩点 【模板】
2最小路径覆盖【例题解释】 // 最好 和本题 对比 ,搞清楚什么时候是 可相交的最小路径覆盖,什么时候是 不可相交的最小路径覆盖。 一般图的最小路径覆盖求的是不相交的(题目上会有说明,不会走重复点之类的提示)。。而这道题不是,没有强调不能够重复,所以我们要用floyd求一下才可以。,
3floyd 求传递闭包 例题
floyd 求传递闭包作用 ,由于我也是第一次接触这个(虽然离散课刚结课Σ( ° △ °|||)︴) 。就找了个感觉还算典型的例题来理解下;
对DAG 图的理解
1建议 将这道题和下面这道题仔细对比一下
例题点我
DAG 图中,从一个入度为0 的点 确实是可以到达 与其相连的所有点,但是这道题目是,拜访一遍就是确确实实的走一遍(选择一个方向的话,走到头就走不动了,不能够返回原入度为0的点),而上面的例题是 从入度为0的点 可以向好多方向打电话,相当于走了好几条路。 所以这是不同的。。
所以就引出了 最小路径覆盖(最大匹配求解)这个概念。==》走 互不相交的路径,问最少要走几条才可以走完全部的点。
是不是和这道题已经很接近了,但还不是,这道题题的意思就是 可以走重复的路径,即是要 求可以相交的型的 最小路径覆盖(所以这里要先用Floyd求传递闭包,然后在求解 最大匹配)。最后再来一下 01 背包求一下就行。
先来个dalao的代码
#include <bits/stdc++.h> using namespace std; const int MAXN=130100; struct node { int v; int nxt; }; node edge[MAXN];//边数 int head[550]; int n,m,k; int Stop,Bcnt,Dindex;//栈头,强通块数,时间戳 int DFN[550],LOW[550];//首时间戳,最近回溯点(根) int Stap[550];//答案栈 int instack[550];//是否在栈中 int Belong[550];//这个点属于第几个强连通块(点) int cnt=0; void add_edge(int u,int v) { edge[cnt].v=v; edge[cnt].nxt=head[u]; head[u]=cnt; cnt++; } void tarjan(int i) { int j; DFN[i]=LOW[i]=++Dindex; instack[i]=1; Stap[++Stop]=i; for (int e=head[i]; e!=-1; e=edge[e].nxt) { j=edge[e].v; if (!DFN[j])//儿子没遍历 { tarjan(j);//遍历 if (LOW[j]<LOW[i])//如果儿子已经形成环 LOW[i]=LOW[j];//父亲也要在回溯的时候进入环 } else if (instack[j]&&DFN[j]<LOW[i])//邻接的在栈里,所以是大大 LOW[i]=DFN[j];//把这个点归到大大那 } if (DFN[i]==LOW[i])//这个点的根是自己 { Bcnt++;//多了一个强连通分量 do { j=Stap[Stop--];//退栈 instack[j]=0;//标记 Belong[j]=Bcnt;//标记 } while (j!=i); } } void solve() { int i; Stop=Bcnt=Dindex=0;//栈头,强通块数,时间戳 memset(DFN,0,sizeof(DFN)); memset(instack,0,sizeof(instack)); for (int i=1; i<=n; i++) if (!DFN[i]) tarjan(i); } struct bian { int f,t; } st[MAXN]; int aa[550]; int bb[550]; int dp[3050]; bool mp[550][550]; bool vis[550]; int link[550]; bool dfs(int u) { for(int i=1; i<=Bcnt; i++) { if(i==u)continue; if(mp[u][i]&&!vis[i]) { vis[i]=1; if(link[i]==-1||dfs(link[i])) { link[i]=u; return 1; } } } return 0; } int main() { int T; scanf ("%d",&T); while (T--) { scanf ("%d%d%d",&n,&m,&k); memset(head,-1,sizeof(head)); cnt = 0; for (int i=0; i<m; i++) { scanf ("%d%d",&st[i].f,&st[i].t); add_edge(st[i].f,st[i].t); } solve(); memset(mp,0,sizeof(mp)); for (int i=0; i<m; i++) { if (Belong[st[i].f]!=Belong[st[i].t]) { int u = Belong[st[i].f]; int v = Belong[st[i].t]; mp[u][v]=true; } } for(int k=1;k<=Bcnt;++k){ for(int i=1;i<=Bcnt;++i){ for(int j=1;j<=Bcnt;++j){ if(mp[i][k]&&mp[k][j]) mp[i][j]=true; } } } memset(link,-1,sizeof(link)); int ccnt = 0; for(int i=1; i<=Bcnt; i++) { memset(vis,0,sizeof(vis)); if(dfs(i)) ccnt++; } int need=Bcnt-ccnt,sum = 0; //printf("%d\n",need); for (int i=0; i<k; i++) scanf ("%d%d",&aa[i],&bb[i]),sum+=bb[i]; if(sum<need){ printf("-1\n"); continue; } for (int i=0; i<=1010; i++) dp[i]=0x3f3f3f3f; dp[0]=0; int ans=0x3f3f3f3f; for (int i=0; i<k; i++) { for (int j=1010; j>=0; j--) { if (j-bb[i]<0) break; dp[j]=min(dp[j],dp[j-bb[i]]+aa[i]); if (j>=need) ans=min(dp[j],ans); } } printf ("%d\n",ans); } return 0; }
我的代码
#include <bits/stdc++.h> using namespace std; #define inf 0x3f3f3f3f #define LL long long const int M = 1000; const int MAXM = 230100; const double PI = acos(-1.0); const double eps = 1e-8; inline int read(){ int x=0,f=1; char ch=getchar(); while (ch<'0'||ch>'9') { if (ch=='-') f=-1; ch=getchar(); } while (ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*f; } /*--------------------------------------*/ struct Edge { int from,to,next; }edge[MAXM]; int head[M],top; int low[M],dfn[M]; int dfs_clock; int Instack[M]; stack<int>S; int sccno[M],scc_cnt; int n,m,K; v 11499 oid init() { top=0; memset(head,-1,sizeof(head)); while(!S.empty()) S.pop(); } void addedge(int a,int b) // 重边or自环 求scc的时候,不影响,不必要去除 { /*if(a==b) return; int i; for(int i=head[a];i!=-1;i=edge[i].next) if(edge[i].to==b) return;*/ Edge e={a,b,head[a]}; edge[top]=e;head[a]=top++; } void getmap() { int i,j,a,b; while(m--) { scanf("%d%d",&a,&b); addedge(a,b); } } void tarjan(int now) { int nexts; dfn[now]=low[now]=++dfs_clock; S.push(now);Instack[now]=1; for(int i=head[now];i!=-1;i=edge[i].next) { Edge e=edge[i]; if(!dfn[e.to]) { tarjan(e.to); low[now]=min(low[e.to],low[now]); } else if(Instack[e.to]) low[now]= min(low[now],dfn[e.to]); } if(low[now]==dfn[now]) { scc_cnt++; for(;;) { nexts=S.top();S.pop(); sccno[nexts]=scc_cnt; Instack[nexts]=0; if(nexts==now) break; } } } void find_cut(int le,int ri) { int i,j; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(sccno,0,sizeof(sccno)); memset(Instack,0,sizeof(Instack)); scc_cnt=dfs_clock=0; for(i=le;i<=ri;i++) if(!dfn[i]) tarjan(i); } int mp[M][M]; // 邻接矩阵存缩点后的图 void suodian() { memset(mp,0,sizeof(mp)); for(int i=0;i<top;i++) // 一个scc表示一个点来进行缩点 { int now=sccno[edge[i].from]; int nexts=sccno[edge[i].to]; if(now!=nexts) mp[now][nexts]=1; } } int pipei[M],vis[M]; int find(int x) { for(int i=1;i<=scc_cnt;i++) { if(!vis[i]&&mp[x][i]) { vis[i]=1; if(pipei[i]==-1||find(pipei[i])) { pipei[i]=x; return 1; } } } return 0; } // 传递闭包 void floyd() { for(int k=1;k<=scc_cnt;++k){ for(int i=1;i<=scc_cnt;++i){ for(int j=1;j<=scc_cnt;++j){ mp[i][j]= mp[i][j]||mp[i][k]&&mp[k][j]; } } } } int cost[M],num[M]; int dp[4000];///***** void solve() { // 最大匹配 int ge=0; memset(pipei,-1,sizeof(pipei)); for(int i=1;i<=scc_cnt;i++) { memset(vis,0,sizeof(vis)); ge+=find(i); } int need=scc_cnt-ge; //最小路径覆盖==顶点数--最大匹配数 int sum=0; for (int i=0; i<K; i++) { scanf ("%d%d",&cost[i],&num[i]); sum+=num[i]; } if(sum<need){ // 传送 魔法不够 printf("-1\n"); } else { // 01 背包 for (int i=0; i<=1010; i++) dp[i]=inf; dp[0]=0; int ans=inf; for (int i=0; i<K; i++) { for (int j=1010; j>=0; j--) { if (j-num[i]<0) break; dp[j]=min(dp[j],dp[j-num[i]]+cost[i]); if (j>=need) ans=min(dp[j],ans); } } printf ("%d\n",ans); } } int main() { int t; scanf("%d",&t); while(t--) { scanf("%d%d%d",&n,&m,&K); init(); getmap(); find_cut(1,n); suodian(); floyd(); solve(); } return 0; }
相关文章推荐
- 最大报销额 01背包
- HDU1864:最大报销额(类01背包 浮点数处理)
- 动态规划实例(七):01背包问题最大价值
- (hdu step 3.3.1)Big Event in HDU(01背包:N件物品放在容量为V的背包中,第i件物品的费用是c[i],价值是w[i]。问所能获取的最大价值)
- 最大报销额 (HDU 1864)解题报告(DP - 01 - 背包)
- hdu 2126 Buy the souvenirs(01背包求最大容量方法数)
- HDU 1864 最大报销额(01背包,烂题)
- 【01染色法判断二分匹配+匈牙利算法求最大匹配】HDU The Accomodation of Students
- BZOJ 4819: [Sdoi2017]新生舞会 01分数规划 二分图最大权匹配(KM算法)/费用流
- hdu 1864 最大报销额 01背包变形
- 九度OJ 1025:最大报销额 (01背包、DP)
- HDU OJ 1864 最大报销额 01背包
- HDU1864_最大报销额(背包/01背包)
- hdu 1864 最大报销额(01背包d )
- HDU-1864 - 最大报销额 - 01背包
- 最大报销额(背包01)
- 九度OJ 1025:最大报销额 (01背包、DP)
- HDU1864 最大报销额 01背包
- 01背包,求最大值
- 最大报销额(可转化成01背包)