题意:有n个点和m条有向边构成的网络。每条边有两个花费:
d:毁坏这条边的花费 b:重建一条双向边的花费
寻找这样两个点集,使得点集s到点集t满足 毁坏全部S到T的路径的费用和 > 毁坏全部T到S的路径的费用和 + 重建这些T到S的双向路径的费用和。
思路1:
然后这个无源汇带上下界网络流的可行流问题的求解方法见这里~~
建图就是上面说的那样啦~最后推断有没有可行流就是求一下我们所构造的这个新的网络的最大流~然后推断一下这个最大流是否满流~(即推断最大流是否和附加源点的流出总量相等~~)
code:
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<vector> #include<string> #include<queue> #include<map> #include<set> #include<cmath> #include<cstdlib> using namespace std; #define INF 0x3f3f3f3f #define PI acos(-1.0) #define mem(a, b) memset(a, b, sizeof(a)) typedef pair<int,int> pii; typedef long long LL; //------------------------------ const int maxn = 210; int n,m; struct Edge{ int to, cap, rev;//终点 容量 反向边 Edge(int to_ = 0, int cap_ = 0, int rev_ = 0){ to = to_; cap = cap_; rev = rev_; } }; vector<Edge> g[maxn]; int level[maxn];//顶点到源点的距离标号 int iter[maxn]; void add_Edge(int from, int to, int cap){ Edge tmp1(to, cap, g[to].size()); g[from].push_back(tmp1); Edge tmp2(from, 0, g[from].size()-1); g[to].push_back(tmp2); } void bfs(int s){ memset(level, -1, sizeof(level)); queue<int> q; level[s] = 0; q.push(s); while(!q.empty()){ int v = q.front(); q.pop(); for(int i = 0; i < g[v].size(); i++){ Edge& e = g[v][i]; if(e.cap > 0 && level[e.to] < 0){ level[e.to] = level[v] + 1; q.push(e.to); } } } } int dfs(int v, int t, int f){ if(v == t) return f; for(int& i = iter[v]; i < g[v].size(); i++){ Edge& e = g[v][i]; if(e.cap > 0 && level[v] < level[e.to]){ int d = dfs(e.to, t, min(e.cap, f)); if(d > 0){ e.cap -= d; g[e.to][e.rev].cap += d; return d; } } } return 0; } int max_flow(int s, int t){ int flow = 0; for(;;){ bfs(s); if(level[t] < 0) return flow; memset(iter, 0, sizeof(iter)); int f; while((f = dfs(s, t, INF)) > 0){ flow += f; } } } //------------------上面是最大流模板部分 正确---------------------- int sum; int mi[maxn];//这个是用来保存每一个点的 入度-出度 的值,就是论文里的M(i); void init(){ for(int i = 0; i < maxn; i++){//网络流里的清空。不要忘记 g[i].clear(); } memset(mi, 0, sizeof(mi)); scanf("%d%d",&n,&m); int u, v, d, b; for(int i = 1; i <= m; i++){ scanf("%d%d%d%d",&u, &v, &d, &b); mi[u]-=d; mi[v]+=d; add_Edge(u, v, b);//加边加上的是每条边的下界值 } sum = 0;//sum保存的是源点的全部流出流量 for(int i = 1; i <= n; i++){ if(mi[i] < 0) add_Edge(i, n+1, -mi[i]); else if(mi[i] > 0) { add_Edge(0, i, mi[i]); sum += mi[i]; } } } void solve(){ int ans = max_flow(0, n+1); if(ans == sum) printf("happy "); else printf("unhappy "); } int main(){ int t; int cas = 0; scanf("%d",&t); while(t--){ init(); printf("Case #%d: ",++cas); solve(); } return 0; }
思路2:
阔以试想~假设对于全部单一的一个点组成的集合S来说,都不能满足
S到T的路径的费用和 > 毁坏全部T到S的路径的费用和 + 重建这些T到S的双向路径的费用和 的话,那么随意的点的集合构成的S集合也一定并不能满足上述条件~~~反之假设存在集合满足上述条件的话,就一定存在单一的点满足上述条件~~~~
这样我们事实上仅仅用枚举点然后推断就好啦。。。。。
Orz。。。
code:
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<vector> using namespace std; const int maxn = 205; int n, m; int D[maxn][maxn], B[maxn][maxn]; vector<int> in[maxn], out[maxn]; void init(){ for(int i = 0; i < maxn; i++){ in[i].clear(); out[i].clear(); } memset(D,0,sizeof(D)); memset(B,0,sizeof(B)); scanf("%d%d",&n,&m); int u, v, dd, bb; for(int i = 1; i <= m; i++){ scanf("%d%d%d%d",&u,&v,&dd,&bb); out[u].push_back(v); in[v].push_back(u); D[u][v] = dd; B[u][v] = bb; } } bool deal(int u){ int x = 0; for(int i = 0; i < in[u].size(); i++){ int v = in[u][i]; x += D[v][u]; } int y = 0; for(int i = 0; i < out[u].size(); i++){ int v = out[u][i]; y = y + D[u][v] + B[u][v]; } if(y < x) return true; return false; } void solve(){ for(int i = 1; i <= n; i++){ if(deal(i)){ printf("unhappy "); return; } } printf("happy "); } int main(){ int t; int cas = 0; scanf("%d",&t); while(t--){ init(); printf("Case #%d: ",++cas); solve(); } return 0; }