BZOJ 4155 Humble Captains

BZOJ 4155 Humble Captains

题目传送门1

题意

每天下午放学时都有n个zky冲出教室去搞基。搞基的zky们分成两队,编号为1的zky是1号队的首领,编号为2的zky是2号队的首领。其他的zky可以自由选择是去1队还是2队。zky们当中有m对zky是好基友(同一个zky可能在多对“好基友”关系中),如果一对好基友在同一个队,那么这个队的战斗力就+1。
现在你要解决两个问题:1.两队战斗力之和最大是多少?2.两队战斗力之差的绝对值最小是多少?
注意:这两个问题是不相关的。也就是说,问1和问2的方案可以是不一样的。
\((1 \leq n \leq 200,1 \leq t \leq 250)\)\(t\)为数据组数。

题解

这道题一共两个问,实际上就是两道题目综合起来,第一问就是比较裸的最小割,跑一边最大流即可,第二问就和 阿狸和桃子的游戏 很像了,不过是要求差值的最小值,所以就不是贪心地去选了,而是进行\(Dp\),获得最优的方案,可以用\(bitset\)进行优化。还有,别忘记把边数开大,题目中没有给出\(m\)的大小,所以这可能是达到\(n^2\)级别的。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
#define PAUSE printf("Press Enter key to continue..."); fgetc(stdin);
const int N=600;
const int M=1e5+500;
int n,m,T,tot=1;
struct edge {
    int to,nxt,w;
}E[M];
int head[N],dis[N],d[N];
bitset<50000>f;
/*==================Define Area================*/
void addedge(int u,int v,int w) {
    E[++tot].to=v;E[tot].nxt=head[u];head[u]=tot;E[tot].w=w;
    E[++tot].to=u;E[tot].nxt=head[v];head[v]=tot;E[tot].w=0;
} 

int Bfs() {
    memset(dis,-1,sizeof dis);
    queue<int>Q;
    Q.push(1);
    dis[1]=0;
    while(!Q.empty()) {
        int o=Q.front();Q.pop();
        for(int i=head[o];~i;i=E[i].nxt) {
            int to=E[i].to;
            if(dis[to]==-1&&E[i].w) {
                dis[to]=dis[o]+1;
                Q.push(to);
            }
        }
    }
    return dis[2]!=-1;
}

int Dfs(int u,int flow) {
    if(u==2) return flow;
    int used=0,k;
    for(int i=head[u];~i;i=E[i].nxt) {
        int to=E[i].to;
        if(dis[to]==dis[u]+1&&E[i].w) {
            k=Dfs(to,min(E[i].w,flow-used));
            E[i].w-=k;E[i^1].w+=k;
            used+=k;
            if(used==flow) return flow;
        }
    }
    if(!used) dis[u]=-1;
    return used;
}


int main() {
    read(T);
    while(T--) {
        memset(head,-1,sizeof head);tot=1;
        memset(d,0,sizeof d);
        read(n);read(m);
        int ans=m;
        for(int i=1,u,v;i<=m;i++) {
            read(u);read(v);
            addedge(u,v,1);
            addedge(v,u,1);
            d[u]++;d[v]++;
        }
        while(Bfs()) ans-=Dfs(1,0x3f3f3f3f);
        printf("%d ",ans);
        f.reset();
        f[n*n+d[1]-d[2]]=1;
        for(int i=3;i<=n;i++) f=(f<<d[i])|(f>>d[i]);
        for(int i=0;i<=n*n;i++) {
            if(f[i+n*n]||f[n*n-i]) {printf("%d\n",i/2);break;}
        }   
    }
    return 0;
}

转载于:https://www.cnblogs.com/Apocrypha/p/9582284.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值