1. 程式人生 > >網絡流 24題 試題庫問題

網絡流 24題 試題庫問題

title include freopen 滿足 編號 turn 容易 lan clu

試題庫問題

題目描述

假設一個試題庫中有n道試題。每道試題都標明了所屬類別。同一道題可能有多個類別屬性。現要從題庫中抽取m道題組成試卷。並要求試卷包含指定類型的試題。試設計一個滿足要求的組卷算法。
具體的描述見輸入格式。

輸入格式

文件第1行有2個正整數k和n (2 <=k<= 20,k<=n<= 1000) k表示題庫中試題類型總數,n表示題庫中試題總數。第2行有k個正整數,第i個正整數表示要選出的類型i的題數。這k個數相加就是要選出的總題數m。接下來的n行給出了題庫中每個試題的類型信息。每行的第1個正整數p表明該題可以屬於p類,接著的p個數是該題所屬的類型號。
(註意,在最終的試卷中,每題充當一個類別的題目)

輸出格式

文件第i行輸出 “i:”後接類型i的題號。如果有多個滿足要求的方案,只要輸出1個方案。如果問題無解,則輸出“No Solution!”。

輸入樣例

315
3 3 4
2 1 2
1 3
1 3
1 3
1 3
3 1 2 3
2 2 3
2 1 3
1 2
1 2
2 1 2
2 1 3
2 1 2
1 1
3 1 2 3

輸出樣例 2208.out

1:1 6 8
2: 7 9 10
3: 2 3 4 5

這題呢,就沒有前兩題那麽套路啦。

題目大意:

我認為還是有必要說一下的,畢竟它講得有點小抽象。首先,有k種試題,n道試題。每種試題要選i題。接著給出的是每一道題目可以歸為p類的試題,接著是p個(1~k)的種類。

最後得出可以怎麽選,每種題目可以選哪i道題。

構圖:

和上面的構圖差不多,同樣是二分圖,把試題種類放左邊,與s相連,邊權為a[i],試題放右邊(註意編號),把種類與能夠作為此種類的題目相連,邊權為1。

大體思路:

同樣是跑一次最大流,然後輸出方案。與前兩題的地方略有不同的地方就是要輸出方案。

如何輸出方案:

首先要知道,在殘余網絡中,如果是最大流跑過的地方,到達的題目,反向邊應該為1(最優方案需要選擇這個點),因此,只要從每一個種類節點出發,搜到反向邊為1的點就是可行的點,就可以輸出,前提是要為反向邊,不然很容易會輸出s(0),不小心搜回去。

PS:

打了兩次最大流的題目以後,後來的跑最大流都是直接復制粘貼的了,不過最好不要這樣咯,小心參數的錯誤。

代碼如下

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=2005,oo=10000000;
int ans,s,sum,t,n,k,x,v[maxn],a[maxn],b,cur=-1,head[maxn];

struct exam
{
	int va,next,to;
}edge[maxn*maxn];

void add(int from,int to,int va)
{
	cur++;
	edge[cur].to=to;
	edge[cur].va=va;
	edge[cur].next=head[from];
	head[from]=cur;
}
 
int dfs(int now,int mi)
{
	if(now==t) return mi;
	if(v[now]==1)	return 0;
	v[now]=1;
	int h=head[now];
	while(h!=-1)
	{
		int to=edge[h].to,va=edge[h].va;
		if(va!=0)
		{
			int k;
			k=dfs(to,min(va,mi));
			if(k!=0)
			{
				edge[h].va-=k;
				edge[h^1].va+=k;
				return k;
			}
		}
		h=edge[h].next;
	}
	return 0;
}

void print()
{
	for(int i=n+1;i<=n+k;i++)
	{
		cout<<i-n<<":"<<" ";
		int h=head[i];
		while(h!=-1)
		{
			int to=edge[h].to,va=edge[h].va;
			if(va==1)
			{
				cout<<to<<" "; 
			}
			h=edge[h].next;
		}
		cout<<endl;
	}
}//輸出 每個種類搜一次

void start()
{
	while(1)
	{
		memset(v,0,sizeof(v));
		int res;
		res=dfs(0,oo);
		if(res==0) break;
		ans+=res;
	}
	if(ans!=sum) cout<<"No Solution!"<<endl;//如果不等於需要的題數(a[i]+...+a[k])
	else
	{
		print();
	}		
}//跑最大流

int main()
{
	freopen("2208.in","r",stdin);
	freopen("2208.out","w",stdout);
	cin>>k>>n;
	s=0,t=k+n+1;
	memset(head,-1,sizeof(head));
	for(int i=1;i<=k;i++)
	{
		cin>>a[i];
		sum+=a[i];
		add(i+n,t,a[i]);
		add(t,i+n,0);
	}
	for(int i=1;i<=n;i++)
	{
		cin>>b;
		for(int j=1;j<=b;j++)
		{
			cin>>x;
			add(i,x+n,1);//把種類的下標+n,避免編號的重復
			add(x+n,i,0);
		}
	}
	for(int i=1;i<=n;i++)
	{
		add(s,i,1);
		add(i,s,0); 
	}//構圖
	start();
	return 0;
}

  

網絡流 24題 試題庫問題