题面传送机
Sol
网络流 网络流 可怕的网络流
建图方式
1,建立超源(S)汇(E)点
2,每一个题型向E连有向边 流量为所需题目个数
3,每道题向所属题型连有向边 流量为1
4,S向题连有向边 流量为1
判断是否成卷就看最大流是否大于等于总需题目量了
求答案的方法(从代码里搬过来的)
/*************************
两种求答案的方法 , 一个是记录每一条边
的两点 , 再枚举所有边如果这条边是由类
型射向题目的 , 且边权 > 0那么就是符合的
另外一个是枚举每一个类型找边权大于0的边
剩下的代码表示
**************************/
Code
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstring>
#define inc(i) (i = -(~i))
#define dec(i) (i = ~(-i))
using namespace std;
namespace fast
{
inline char Getchar()
{
static char buf[100001] , *p1 = buf , *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++;
}
inline int read()
{
int num = 0; char c = 0;
while(!isdigit(c)) c = Getchar();
while(isdigit(c)) num = num * 10 + c - '0' , c = Getchar();
return num;
}
}
using namespace fast;
const int N = 1000 + 7 , M = 50000 + 7 , INF = 1e9 + 7;
int S , E , Dep[N * 2] , Sum;
int k , n , Head[N * 2] , Node[M] , W[M] , Next[M] , tot = 1;
struct Queue
{
int l , r , Que[M];
inline void cls() {l = 100 , r = 99;}
inline bool full() {return r >= l;}
inline int top() {return Que[l];}
inline void pop() {inc(l);}
inline void push(int x) {Que[inc(r)] = x;}
}Q;
inline void Add(int u , int v , int w)
{
Next[inc(tot)] = Head[u] , Head[u] = tot , Node[tot] = v , W[tot] = w;
Next[inc(tot)] = Head[v] , Head[v] = tot , Node[tot] = u , W[tot] = 0;
}
inline bool BFS()
{
memset(Dep , 0 , sizeof(Dep)) , Q.cls();
int u , v;
Dep[S] = 1 , Q.push(S);
while(Q.full())
{
u = Q.top(); Q.pop();
for(int i = Head[u] ; i ; i = Next[i])
{
v = Node[i];
if(!Dep[v] && W[i])
{
Dep[v] = Dep[u] + 1;
Q.push(v);
}
}
}
return Dep[E];
}
inline int DFS(int u , int lastc)
{
if(u == E || !lastc) return lastc;
int v , Flow , Now = 0;
for(int i = Head[u] ; i ; i = Next[i])
{
v = Node[i];
if(Dep[u] == Dep[v] - 1 && W[i])
{
Flow = DFS(v , min(lastc , W[i]));
if(Flow)
{
Now += Flow;
W[i] -= Flow;
W[i ^ 1] += Flow;
if(Now == lastc) break;
}
}
}
return Now;
}
inline bool Max_Flow()
{
int Ans = 0;
while(BFS())
Ans += DFS(S , INF);
return Ans >= Sum;
}
int main()
{
k = read() , n = read();
S = k + n + 1 , E = S + 1;
for(int i = 1 , w ; i <= k ; inc(i))
w = read() , Add(i , E , w) , Sum += w;
for(int i = 1 , p , q ; i <= n ; inc(i))
{
p = read() + 1;
while(dec(p))
q = read() , Add(i + k , q , 1);
Add(S , i + k , 1);
}
if(Max_Flow())
{
/***********************************
两种求答案的方法 , 一个是记录每一条边
的两点 , 再枚举所有边如果这条边是由类
型射向题目的 , 且边权 > 0那么就是符合的
另外一个是枚举每一个类型找边权大于0的边
剩下的代码表示
************************************/
for(int u = 1 , v ; u <= k ; inc(u))
{
printf("%d:" , u);
for(int i = Head[u] ; i ; i = Next[i])
{
v = Node[i];
if(v <= k + n && W[i])
printf(" %d" , v - k);
}
puts("");
}
}
else
puts("No Solution!");
return 0;
}