Stock Charts(Google Code Jam 2009 Round2 C)二分图最大匹配

来源:互联网 发布:阿里云机顶盒刷机固件 编辑:程序博客网 时间:2024/04/30 13:06
来自《挑战程序设计竞赛》

1.题目原文

https://code.google.com/codejam/contest/204113/dashboard#s=p2

You're in the middle of writing your newspaper's end-of-year economics summary, and you've decided that you want to show a number of charts to demonstrate how different stocks have performed over the course of the last year. You've already decided that you want to show the price of n different stocks, all at the same k points of the year.

simple chart of one stock's price would draw lines between the points (0, price0), (1, price1), ... , (k-1, pricek-1), where pricei is the price of the stock at the ith point in time.

In order to save space, you have invented the concept of an overlaid chart. An overlaid chart is the combination of one or more simple charts, and shows the prices of multiple stocks (simply drawing a line for each one). In order to avoid confusion between the stocks shown in a chart, the lines in an overlaid chart may not cross or touch.

Given a list of n stocks' prices at each of k time points, determine the minimum number of overlaid charts you need to show all of the stocks' prices.

Input

The first line of input will contain a single integer T, the number of test cases. After this will follow T test cases on different lines, each of the form:

n kprice0,0 price0,1 ... price0,k-1price1,0 price1,1 ... price1,k-1...pricen-1,0 pricen-1,1 ... pricen-1,k-1

Where pricei,j is an integer, the price of the ith stock at time j.

Output

For each test case, a single line containing "Case #X: Y", where X is the number of the test-case (1-indexed) and Y is the minimum number of overlaid charts needed to show the prices of all of the stocks.

Limits

1 ≤ T ≤ 100
2 ≤ k ≤ 25
0 ≤ pricei,j ≤ 1000000

Small Input

1 ≤ n ≤ 16

Large Input

1 ≤ n ≤ 100

Sample


Input 
 
Output 
 3
3 4
1 2 3 4
2 3 4 6
6 5 4 3
3 3
5 5 5
4 4 6
4 5 4
5 2
1 1
2 2
5 4
4 4
4 1
Case #1: 2
Case #2: 3
Case #3: 2

2.解题思路

我们可以试想一下,以股票为顶点,两支股票能否花在一张图里来决定是否连边。例如,在由于线段相交或者相接而不能画在一张图里的两只股票之间连一条边。这个问题就转化成了求图的最小着色数问题。不过前一半求解图的最小着色数是NP困难的。可以利用问题的性质尝试其他解法。如果两个折线图不相交,其中一条折线必在另一条折线上面。可以利用这条性质来解题。
当股票i可以画在股票j上方时,我们可以从顶点i向顶点j连一条边,这样就得到了一个DAG。考虑DAG上的一条路径,易发现在一条路径上的顶点可以画在一幅图中。问题又可以进一步转化为,使用尽可能少的路径覆盖所有顶点。
路径的条数和路径起点的个数相等,因此我们试着最小化路径起点的个数。由于对于不是起点的顶点一定在另外一个顶点作为路径的前驱顶点,因此只需要最大化这样的顶点的个数就可以了。
考虑左右各有n个顶点的二分图,在前面所建的图中,如果顶点i有到顶点j的边,就在二分图中连接左边的i和右边的j。可以看做每个顶点拆分成两个得到。
按照上述做法,可以得到若干条路径,并且匹配中包含的边数和不是起点的顶点数相等。因此求最大匹配,再用N减去这个值,就得到了最小路径覆盖的路径数。

3.AC代码

#include <algorithm>#include <cctype>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iomanip>#include <iostream>#include <map>#include <queue>#include <string>#include <set>#include <vector>using namespace std;#define maxk 30#define maxn 105#define MAX_V 205int N,K;int P[maxn][maxk];//二分图匹配模板int V;//顶点数vector<int> G[MAX_V];//图的邻接表表示int match[MAX_V];//所匹配的顶点bool used[MAX_V];//DFS中用到的访问标记//向图中增加一条连接u和v的边void add_edge(int u,int v){    G[u].push_back(v);    G[v].push_back(u);}//通过DFS寻找增广路bool dfs(int v){    used[v]=true;    for(int i=0;i<G[v].size();i++){        int u=G[v][i],w=match[u];        if(w<0||!used[w]&&dfs(w)){            match[v]=u;            match[u]=v;            return true;        }    }    return false;}//求解二分图的最大匹配int bipartite_matching(){    int res=0;    memset(match,-1,sizeof(match));    for(int v=0;v<V;v++){        if(match[v]<0){            memset(used,0,sizeof(used));            if(dfs(v)){                res++;            }        }    }    return res;}//二分图匹配模板void solve(){    V=N*2;    for(int i=0;i<V;i++){        G[i].clear();    }    for(int i=0;i<N;i++){        for(int j=0;j<N;j++){            if(i==j) continue;            bool ok=true;            for(int k=0;k<K;k++){                if(P[j][k]>=P[i][k]){                    ok=false;                }            }            if(ok){                add_edge(i,N+j);            }        }    }    int ans=N-bipartite_matching();    printf("%d\n",ans);}int main(){    freopen("C-large-practice.in","r",stdin);    freopen("C-large-practice.out","w",stdout);    int t,kase=0;    scanf("%d",&t);    while(t--){        scanf("%d%d",&N,&K);        for(int i=0;i<N;i++){            for(int k=0;k<K;k++){                scanf("%d",&P[i][k]);            }        }        printf("Case #%d: ",++kase);        solve();    }    return 0;}



0 0
原创粉丝点击