bzoJ2661 连连看(网络流)

时间限制:1秒 内存限制:64M
【问题描述】

  凡是考智商的题里面总会有这么一种消除游戏。不过现在面对的这关连连看可不是QQ游戏里那种考眼力的游戏。我们的规则是,给出一个闭区间[a,b]中的全部整数,如果其中某两个数x,y(设x>y)的平方差x^2-y^2是一个完全平方数(即x^2-y^2=z^2),并且y与z互质,那么就可以将x和y连起来并且将它们一起消除,同时得到x+y点分数。那么过关的要求就是,消除的数对尽可能多的前提下,得到足够的分数。快动手动笔算一算吧。

【输入格式】

  只有一行,两个整数,分别表示a,b。

【输出格式】

  两个数,可以消去的对数,及在此基础上能得到的最大分数。

【输入样例】

1 15

【输出样例】

2 34

【数据范围】

对于30%的数据,1<=a,b<=100
对于100%的数据,1<=a,b<=1000

【来源】

BzOJ2661

一道简单的网络流,每个点分成2个一个入点一个出点,如果2个数满足条件就分别从1个的入点直接连边到一个的出点就可以了(当然源点连每个入点,每个出点连汇点),边权是2个数的和,其他边权值全部是0,最后求个最大费用最大流就出来了。

详细代码如下:

#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<vector>
using namespace std;
const int maxn=2005;
const int inf=200000005;

struct edge
{
    int u,v,f,c,w,next;
}e[maxn*maxn];
vector<int>g[maxn];
int f[maxn],cnt=-1,q[maxn*maxn],d[maxn],fa[maxn],vis[maxn];
int a,b,s,t;

void add(int u,int v,int c,int w)
{
    e[++cnt]=(edge){u,v,c,0,w,f[u]};f[u]=cnt;
    e[++cnt]=(edge){v,u,0,0,-w,f[v]};f[v]=cnt;
}
int gcd(int x,int y)
{
    return y==0?x:gcd(y,x%y);
}
bool work(int x,int y)
{
    int k=sqrt(y*y-x*x);
    if(y*y-x*x!=k*k) return 0;
    return gcd(k,x)==1;
}
int spfa()
{
    memset(vis,0,sizeof(vis));
    for(int i=s;i<=t;i++) d[i]=-inf;
    int frond=0,root=0;
    fa[s]=-1;
    q[root++]=s;
    d[s]=0;
    while(frond!=root)
    {
        int i=q[frond++];
        vis[i]=0;
        for(int k=f[i];k!=-1;k=e[k].next)
        {
            int j=e[k].v,c=e[k].w;
            if(d[j]>=d[i]+c||e[k].f==e[k].c) continue;
            d[j]=d[i]+c;
            fa[j]=k;
            if(vis[j]) continue;
            vis[j]=1;
            q[root++]=j;
        }
    }
    return d[t];
}
void dinic()
{
    int flow=0,cost=0;
    while(1)
    {
        int c=spfa();
        if(c==-inf) break;
        cost+=c;
        flow++;
        int id=fa[t];
        while(id!=-1)
        {
            e[id].c++;
            e[id^1].c--;
            id=fa[e[id].u];
        }
    }
    printf("%d %d",flow/2,cost/2);
}
void dfs(int i,int id)
{
    if(vis[i]) printf("wrong!\n");
    vis[i]=id;
    int tt=g[i].size();
    for(int k=0;k<tt;k++)
    {
        int j=g[i][k];
        if(j==fa[i]) continue;
        fa[j]=i;
        dfs(j,3-id);
    }
}
int main()
{
    //freopen("linking.in","r",stdin);
    //freopen("linking.out","w",stdout);
    scanf("%d%d",&a,&b);
    s=0,t=2*b+1;
    int ans=0;
    for(int i=s;i<=t;i++) f[i]=-1;
    for(int i=a;i<=b;i++)
    for(int j=i+1;j<=b;j++)
    if(work(i,j))
    {
        add(i,j+b,1,i+j);
        add(j,i+b,1,i+j);
    }
    for(int i=a;i<=b;i++) 
    {
        add(s,i,1,0);
        add(i+b,t,1,0);;
    }
    dinic();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值