2015-2016 下半学期 第六周 训练

按照做题顺序。

1、hdu2444

题意:

首先判断是否是二分图,如果不是的话输出No,如果是的话输出最大匹配。

题解:

按题意那么写。

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
#include <set>
#include <stack>
#include <map>
#include <ctime>
#include <bitset>
#define LL long  long
#define db double
#define EPS 1e-15
#define inf 1e10

using namespace std;

struct edge{
	int to,nxt;
}e[4020030];
int n,m,cnt;
int head[2005],link[2005];
bool vis[2005],col[2005];
void init(){
	cnt=0;
	memset(head,-1,sizeof(head));
	memset(col,0,sizeof(col));
}
void add(int u,int v){
	e[cnt].to=v, e[cnt].nxt=head[u],head[u]=cnt++;
	e[cnt].to=u, e[cnt].nxt=head[v],head[v]=cnt++;
}
bool judge(int u){
	for (int i=head[u];~i;i=e[i].nxt){
		int v=e[i].to;
		if (!col[v]){
			col[v]=!col[u];
			if (!judge(v)) return 0;
		}
		else if (col[v]==col[u])
			return 0;
	}
	return 1;
}
bool dfs(int u){
	for (int i=head[u];~i;i=e[i].nxt){
		int v=e[i].to;
		if (!vis[v]){
			vis[v]=1;
			if (link[v]==-1 || dfs(link[v])){
				link[v]=u;
				return 1;
			}
		}
	}
	return 0;
}
int match(){
	int ans=0;
	memset(link,-1,sizeof(link));
	for (int i=1;i<=n;i++){
		memset(vis,0,sizeof(vis));
		if (dfs(i)) ans++;
	}
	return ans;
}
int main(){
	while (scanf("%d%d",&n,&m)!=EOF){
		init();
		for (int i=1;i<=m;i++){
			int u,v;
			scanf("%d%d",&u,&v);
			add(u,v);
		}
		col[1]=1;
		if (!judge(1)) puts("No");
        else printf("%d\n",match()/2);
	}
	return 0;
}

2、

题意:

和上面那题一个意思,建图恶心点。

题解:

同上

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
#include <set>
#include <stack>
#include <map>
#include <ctime>
#include <bitset>
#define LL long  long
#define db double
#define EPS 1e-15
#define inf 1e10

using namespace std;

struct edge{
	int to,nxt;
}e[10010];
int n,m,cnt;
int head[110];
int col[110];
int mp[110][110];
void init(){
	cnt=0;
	memset(head,-1,sizeof(head));
	memset(col,-1,sizeof(col));
}
void add(int u,int v){
	e[cnt].to=v, e[cnt].nxt=head[u],head[u]=cnt++;
	e[cnt].to=u, e[cnt].nxt=head[v],head[v]=cnt++;
}
bool judge(int u,int clo){
	col[u]=clo;
	for (int i=head[u];i!=-1;i=e[i].nxt){
		int v=e[i].to;
		if (col[v]!=-1){
			if (col[v]==clo) return 0;
			continue;
		}
		if (!judge(v,!clo)) return 0;
	}
	return 1;
}
int main(){
	while (scanf("%d",&n)==1){
		memset(mp,0,sizeof(mp));
		int x;
		for (int i=1;i<=n;i++){
			while (scanf("%d",&x) && x)
				mp[i][x]=1;
		}
		init();
		for (int i=1;i<=n;i++)
			for (int j=i+1;j<=n;j++)
				if (mp[i][j]==0 || mp[j][i]==0){
					add(i,j);
				}
		bool flag=1;
		for (int i=1;i<=n;i++){
			if (col[i]==-1 && judge(i,0)==0){
				flag=0; 
				break;
			}
		}
		if (flag) puts("YES");
		else puts("NO");
	}
	return 0;
}
3、poj2594

题意:

最小路径覆盖。

题解:

二分图最小路径覆盖=顶点数-二分图最大匹配。

注意 此题中点可以重复经过,所以用floyd传递闭包维护一下连通性,给二分图上增加一些边。

代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long  long
#define db double
#define EPS 1e-15
#define inf 1e9
using namespace std;
const int MAXN = 550;

bool Map[MAXN][MAXN],Mask[MAXN];
int NX,NY;
int cx[MAXN],cy[MAXN];

void Floyd(int N)       //求传递闭包
{
    for(int k = 1; k <= N; ++k)
    {
        for(int i = 1; i <= N; ++i)
        {
            for(int j = 1; j <= N; ++j)
            {
                if(i != j && Map[i][k] && Map[k][j])
                    Map[i][j] = 1;
            }
        }
    }
}

int FindPath(int u)
{
    for(int i = 1; i <= NY; ++i)
    {
        if(Map[u][i] && !Mask[i])
        {
            Mask[i] = 1;
            if(cy[i] == -1 || FindPath(cy[i]))
            {
                cy[i] = u;
                cx[u] = i;
                return 1;
            }
        }
    }
    return 0;
}

int MaxMatch()  //二分图最大匹配
{
    for(int i = 1; i <= NX; ++i)
        cx[i] = -1;
    for(int i = 1; i <= NY; ++i)
        cy[i] = -1;

    int res = 0;
    for(int i = 1; i <= NX; ++i)
    {
        if(cx[i] == -1)
        {
            for(int j = 1; j <= NY; ++j)
                Mask[j] = 0;
            res += FindPath(i);
        }
    }
    return res;
}

int main()
{
    int N,M,u,v;
    while(~scanf("%d%d",&N,&M) && (N||M))
    {
        memset(Map,0,sizeof(Map));
        for(int i = 1; i <= M; ++i)
        {
            scanf("%d%d",&u,&v);
            Map[u][v] = 1;
        }
        Floyd(N);
        NX = NY = N;
        printf("%d\n",N - MaxMatch());
    }

    return 0;
}

4、

题意:

能不能把一个每个字符都有权值的负环分割成两部分,当分割后的字符串是回文串时,权值为各字符之和,否则为1。求最大权值。

题解:

扩展KMP 或者 Manacher都可以 只要判断出以i点为中心,r为半径的字符串的权值,然后枚举一下,找到最大值。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
 
using namespace std;
const int MAXN = 500010;
int inde[30];
char str[MAXN];
char Ma[MAXN * 2];
int Mp[MAXN * 2];
int len;
long long value[MAXN];
bool flaga[MAXN];
bool flagb[MAXN];
 
void Manacher(){
    int l = 0;
    Ma[l ++] = '$';
    Ma[l ++] = '#';
    for(int i  = 0; i < len; i ++){
        Ma[l ++] = str[i];
        Ma[l ++] = '#';
    }
    Ma[l] = 0;
    int mx = 0, id = 0;
    for(int i = 0; i < l; i ++){
        Mp[i] = mx > i ? min(Mp[2 * id - i], mx - i) : 1;
        while(Ma[i + Mp[i]] == Ma[i - Mp[i]])
            Mp[i] ++;
        if(i + Mp[i] > mx){
            mx = i + Mp[i];
            id = i;
        }
        if(i - Mp[i] == 0)
            flaga[Mp[i] - 1] = true;
        if(i + Mp[i] == len + len + 2)
            flagb[Mp[i] - 1] = true;
    }
}
 
int T;
 
int main(void){
    cin >> T;
    while(T --){
        for(int i = 0; i < 26; i ++){
            cin >> inde[i];
        }
        memset(flaga, false, sizeof(flaga));
        memset(flagb, false, sizeof(flagb));
        cin >> str;
        len = strlen(str);
        for(int i = 1; i <= len; i ++){
            value[i] = value[i - 1] + inde[str[i - 1] - 'a'];
        }
        Manacher();
        long long ans = 0;
        long long temp = 0;
        for(int i = 1; i < len; i ++){
            if(flaga[i] == true)
                temp += value[i];
            if(flagb[len - i] == true)
                temp += value[len] - value[i];
            ans = ans > temp ? ans : temp;   
            temp = 0;
        }
        cout << ans << endl;
 
    }
}

2、

题意:中文题。

题解:马拉车模板题。

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
#include <set>
#include <stack>
#include <map>
#include <ctime>
#include <bitset>
#define LL long  long
#define db double
#define EPS 1e-15
#define inf 1e10

using namespace std;

const int N = 110009;
char s[N], t[N<<1];
int p[N<<1];

int manacher(int len){
	int id=1,mx=0;
	p[0]=1;
	for (int i=1;i<len;i++){
		if (mx>i) p[i]=min(mx-i,p[2*id-i]);
		else p[i]=1;
		while (t[i+p[i]]==t[i-p[i]])
			p[i]++;
		if (mx<p[i]+i){
			mx=p[i]+i;
			id=i;
		}
	}
	int ans=p[1];
	for (int i=1;i<len;i++){
		if (ans<p[i]) ans=p[i];
	}
	return ans-1;
}
int main(){
	while (~scanf("%s",s)){
		t[0]='@';
		t[1]='#';
		int len=strlen(s);
		for (int i=0;i<len;i++){
			t[i*2+2]=s[i];
			t[i*2+3]='#';
		}
		printf("%d\n",manacher(len*2+2));
	}
	return 0;
}

3、

题意:

要求字符串的一个子串 前1/3和中间1/3是回文,前1/3和后1/3相等。

题解:

由于回文串的性质,我们可以知道 前2/3和后2/3都是回文串,用manacher可以得到每个位置为起点的最长回文串一半的长度。然后从左往右扫一遍,看一下相对于当前位置的回文长度,找后面的位置是否满足要求。

代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long  long
#define db double
#define EPS 1e-15
#define inf 1e9
using namespace std;

const int N = 100007;
int num[N];
int tmp[N << 1];
int len[N << 1];
int n;

int min( int x, int y )
{
    return x < y ? x : y;
}

void convert( int * st, int * dst )
{
    int l = 2 * n;
    dst[0] = -1;
    for ( int i = 1; i <= l; i += 2 )
    {
        dst[i] = -2;
        dst[i + 1] = st[i / 2];
    }
    dst[2 * n + 1] = -2;
    dst[2 * n + 2] = -3;
}

void manacher( int * st, int * dst )
{
    convert( st, dst );
    int l = 2 * n + 1;
    int mx = 0, po = 0;
    for ( int i = 1; i <= l; i++ )
    {
        if ( mx > i )
        {
            len[i] = min( mx - i, len[2 * po - i] );
        }
        else
        {
            len[i] = 1;
        }
        while ( dst[i - len[i]] == dst[i + len[i]] )
        {
            len[i]++;
        }
        if ( len[i] + i > mx )
        {
            mx = len[i] + i;
            po = i;
        }
    }
}

int main ()
{
    int t;
    scanf("%d", &t);
    for ( int _case = 1; _case <= t; _case++ )
    {
        scanf("%d", &n);
        for ( int i = 0; i < n; i++ )
        {
            scanf("%d", num + i);
        }
        manacher( num, tmp );
        int ans = 0, l = 2 * n + 1;
        for ( int i = 1; i <= l; i += 2 )
        {
            for ( int j = i + len[i] - 1; j - i > ans; j -= 2 )
            {
                if ( j - i + 1 <= len[j] )
                {
                    ans = j - i;
                    break;
                }
            }
        }
        ans = ans / 2 * 3;
        printf("Case #%d: %d\n", _case, ans);
    }
    return 0;
}

8、

题意:

n个队员做m道题,第i个小时做第i个题目。Pij表示第i个队员能做出第j个题目的概率。

在任意时刻,任意两个队员的编程时间之差不允许超过1个小时。

问能做出最多题目的期望。

题解:

可以明显的看出,把n个队员作为一个集合,m个题做为一个集合。

因为题意里的第二句话,所以,我们做M/N次费用流求和。

带权二分图匹配。

代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long  long
#define db double
#define EPS 1e-15
#define inf 1e9

using namespace std;

const int ME=1010;
const int MV=100;
struct edge{
    int to,nxt,flow;
    db cost;
}e[ME];
int head[MV],cnt;
queue<int> q;
int cur[MV],pre[MV];
bool used[MV],sign[MV];
int flow,n,m;
db cost,dis[MV];
double p[16][1024];
bool spfa(int s,int t){
    memset(used,0,sizeof(used));
    memset(sign,0,sizeof(sign));
    memset(dis,0,sizeof(dis));
    used[s]=sign[s]=1;
    while (!q.empty()) q.pop();
    q.push(s);
    while (!q.empty()){
        int u=q.front();
        q.pop();
        used[u]=0;
        for (int i=head[u];~i;i=e[i].nxt){
            if (e[i].flow<1) continue;
            int v=e[i].to;
            db c=e[i].cost;
            if (!sign[v] || dis[v]>dis[u]+c){
                dis[v]=dis[u]+c;
                sign[v]=1;
                pre[v]=u;
                cur[v]=i;
                if (used[v]) continue;
                used[v]=1;
                q.push(v);
            }
        }
    }
    return sign[t];
}
void init(){
    cnt=0;
    memset(head,-1,sizeof(head));
}
void add(int u,int v,int flow,db cost){
    e[cnt].to=v, e[cnt].flow=flow, e[cnt].cost=cost;
    e[cnt].nxt=head[u], head[u]=cnt++;

    e[cnt].to=u, e[cnt].flow=0, e[cnt].cost=-cost;
    e[cnt].nxt=head[v], head[v]=cnt++;
}
void solve(int s,int t){
    flow=cost=0;
    while (spfa(s,t)){
        int tmp=t;
        int now=inf;
        while (tmp!=s){
            now=min(now,e[cur[tmp]].flow);
            tmp=pre[tmp];
        }
        flow+=now;
        tmp=t;
        while (tmp!=s){
            int id=cur[tmp];
            cost+=now*e[id].cost;
            e[id].flow-=now;
            e[id^1].flow+=now;
            tmp=pre[tmp];
        }
    }
}
db getcost(){
    return cost;
}
int main(){
    int tt,cas=1;
    scanf("%d",&tt);
    while (tt--){
        scanf("%d%d",&n,&m);
        memset(p,0,sizeof(p));

        for (int i=0;i<n;i++){
            for (int j=0;j<m;j++){
                scanf("%lf",&p[i][j]);
            }
        }

        printf("Case #%d: ",cas++);

        int part=m/n;
        if (m%n!=0) part++;
        db ans=0.0;
        for (int op=0;op<part;op++){
            init();
            int S=2*n+1;
            int T=2*n+2;
            int st=op*n;
            for (int i=0;i<n;i++)
                for (int j=0;j<n;j++)
                    add(i,j+n,1,-p[i][j+st]);
            for (int i=0;i<n;i++)
                add(S,i,1,0);
            for (int i=0;i<n;i++)
                add(i+n,T,1,0);
            solve(S,T);
            ans-=getcost();
        }
        printf("%.5f\n",ans);
    }
    return 0;
}



你可能感兴趣的:(2015-2016 下半学期 第六周 训练)