CSP赛前集训 【DD头子张京华】

DD头子张京华

题目描述:(暂不提供)

这道题考场写费用流写挂了。然后最后输出了 − 1 -1 1草草了事。
T J TJ TJ上说输出 − 1 -1 1零分,但是我却有30?

这道题是一道最小割。
这道题首先建两个 T r i e Trie Trie,一个代表把每个编号按二进制顺序插入,另一个则是把它反着插进去。两点间连一条 I N F INF INF的边。然后对于相同的编号,两个 T r i e Trie Trie上把最后的对应的(也就是相同编号在不同 T r i e Trie Trie上的最后一个节点)点连一条 I N F INF INF的边。

然后对于每一种打钱方式,如果是前缀,就在顺序的 T r i e Trie Trie内将此前缀最后两位的边改为 D D DD DD值。

然后两个 T i r e Tire Tire r o o t root root分别为 S S S T T T

建模就是这样了。考场费用流炸上天。(不过我觉得建模没错啊,就是 R R R

既然建模都没了,这道题就是道最小割的板子了。
注意下连边,最好将所有边求好后在连。

#include <cstdio>
#include <cstring>
using namespace std;

typedef long long ll;

const int N = 3010;
const int INF = 0x3f3f3f3f;

inline int min(int a,int b) { return a < b ? a : b; }

ll ans;
char s[10],ch;
struct Edge{ int to,nxt,flow; } g[N << 1];
int last[N],cnt = 1;
int n,m,val[N][2],tag[10],dep[N]; //val代表Trie上的点连向它Trie上两个儿子的边的权值
int S,T,tot,son[N][2],l,r,q[N << 2];

void add(int u,int v,int w) { g[++cnt] = (Edge){ v,last[u],w }, last[u] = cnt; }
void Add_Edge(int u,int v,int w) { add(u,v,w), add(v,u,0); }

void insert(int x)  //这里是在两颗Trie上插入编号
{
    for(int i = 7;i >= 0; -- i) tag[i] = (1 << i) & x ? 1 : 0;
    int x0 = S,x1 = T;
    for(int i = 7;i >= 0; -- i)
    {
        if(!son[x0][tag[i]]) son[x0][tag[i]] = ++tot;
        x0 = son[x0][tag[i]];
    }
    for(int i = 0;i <= 7; ++ i)
    {
        if(!son[x1][tag[i]]) son[x1][tag[i]] = ++tot;
        x1 = son[x1][tag[i]];
    }
    Add_Edge(x0,x1,INF);
}

void link() //这里是将打钱方式的边加入
{
    q[l = r = 1] = S;
    for(;l <= r; ++ l)
    {
        if(son[q[l]][0]) Add_Edge(q[l],son[q[l]][0],val[q[l]][0]), q[++r] = son[q[l]][0];
        if(son[q[l]][1]) Add_Edge(q[l],son[q[l]][1],val[q[l]][1]), q[++r] = son[q[l]][1];
    }
    q[l = r = 1] = T;
    for(;l <= r; ++ l)
    {
        if(son[q[l]][0]) Add_Edge(son[q[l]][0],q[l],val[q[l]][0]), q[++r] = son[q[l]][0];
        if(son[q[l]][1]) Add_Edge(son[q[l]][1],q[l],val[q[l]][1]), q[++r] = son[q[l]][1];
    }
}

bool bfs()
{
    memset(dep,0,sizeof dep);
    dep[q[l = r = 1] = S] = 1;
    for(;l <= r; ++ l)
        for(int i = last[q[l]];i;i = g[i].nxt)
            if(!dep[g[i].to] && g[i].flow) dep[q[++r] = g[i].to] = dep[q[l]] + 1;
    return dep[T] > 0;
}

int dfs(int x,int flow)
{
    if(x == T) return flow;
    int now = 0;
    for(int i = last[x];i;i = g[i].nxt)
        if(dep[g[i].to] == dep[x] + 1 && g[i].flow)
        {
            int tmp = dfs(g[i].to,min(flow,g[i].flow));
            g[i].flow -= tmp, g[i ^ 1].flow += tmp;
            now += tmp, flow -= tmp;
            if(flow == 0) return now;
        }
    if(now == 0) dep[x] = 0;
    return now;
}

int main()
{
     freopen("dd.in","r",stdin);
     freopen("dd.out","w",stdout);
    
    scanf("%d%d",&n,&m); S = ++tot, T = ++tot, memset(val,INF,sizeof val);
    for(int i = 1,x;i <= n; ++ i) scanf("%d",&x), insert(x);
    ++m; for(int len,w;--m;)
    {
        ch = getchar(); while(ch != 'S' && ch != 'P') ch = getchar();
        scanf(" %s %d",s,&w); len = strlen(s);
        if(ch == 'P')
        {
            int x = S,fg = 1;
            for(int i = 0;i < len - 1; ++ i) 
            {
                if(!son[x][s[i] - '0']) { fg = 0; break; }
                x = son[x][s[i] - '0'];
            }
            if(son[x][s[len - 1] - '0'] && fg) val[x][s[len - 1] - '0'] = min(val[x][s[len - 1] - '0'],w);
        }
        else
        {
            int x = T,fg = 1;
            for(int i = len - 1;i; -- i)
            {
                if(!son[x][s[i] - '0']) { fg = 0; break; }
                x = son[x][s[i] - '0'];
            }
            if(son[x][s[0] - '0'] && fg) val[x][s[0] - '0'] = min(val[x][s[0] - '0'],w);
        }
    }
	
    link(), ans = 0;
    while(bfs() && ans < INF) ans += dfs(S,INF);

    printf("%lld\n",ans >= INF ? -1 : ans);
    fclose(stdin); fclose(stdout);
    return 0;
}

经过 G S M GSM GSM的辅导终于 A A A了。
代码写的很朴实,思路很清晰,所以也会比较长。

但是交上去后遭到 Z J J ZJJ ZJJ的嘲讽。
有兴趣的可以看看他的代码,比较简短。

#include <cstdio>
#include <cstring>
using namespace std;

int n,m,l,r,ans,tot;
const int N = 1 << 10;
int q[N],st[N],dep[N],val[N]; char s[9];
struct EDGE{ int to,next,flow; } g[N << 5];

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

void add(int u,int v,int w)
{ g[++tot] = (EDGE){ v,st[u],w }, st[u] = tot; }

void add_edge(int u,int v,int w) { add(u,v,w), add(v,u,0); }

int BFS()
{
	memset(dep,0,sizeof dep),
	dep[q[l = r = 1] = 1] = 1;
	for(;l <= r; ++ l)
		for(int i = st[q[l]];i;i = g[i].next)
			if(!dep[g[i].to] && g[i].flow)
				dep[q[++r] = g[i].to] = dep[q[l]] + 1;
	return dep[N - 1];
}

int dinic(int x,int flow)
{
	int rest = 0;
	if(x == N - 1) return flow;
	for(int i = st[x],tmp;i;i = g[i].next)
		if(dep[g[i].to] == dep[x] + 1 && g[i].flow)
		{
			tmp = dinic(g[i].to,min(g[i].flow,flow)), rest += tmp,
			flow -= tmp, g[i].flow -= tmp, g[i ^ 1].flow += tmp;
			if(!flow) return rest;
		}
	if(!rest) dep[x] = 0;
	return rest;
}

int main()
{
	freopen("dd.in","r",stdin),
	freopen("dd.out","w",stdout);
	
	memset(val,0x3f,sizeof val),
	scanf("%d%d",&n,&m), tot = 1;
	for(int i = 1,x,y,z;i <= n; ++ i)
	{
		scanf("%d",&x), y = z = 1;
		for(int j = 8;j >= 1; -- j)
			y = y * 2 + (x >> j - 1 & 1);
		for(int j = 1;j <= 8; ++ j)
			z = z * 2 + (x >> j - 1 & 1);
		add_edge(y,N - z,0x3f3f3f3f);
	}
	
	for(int i = 1,x,y;i <= m; ++ i)
	{
		char c; scanf(" %c %s%d",&c,s + 1,&x);
		if(c == 'P')
		{
			y = 1;
			for(int j = 1,p = strlen(s + 1);j <= p; ++ j)
				y = y * 2 + (s[j] ^ '0');
			val[y] = min(val[y],x);
		}
		if(c == 'S')
		{
			y = 1;
			for(int j = strlen(s + 1);j >= 1; -- j)
				y = y * 2 + (s[j] ^ '0');
			val[N - y] = min(val[N - y],x);
		}
	}
	
	for(int i = 1;i <= 255; ++ i)
		add_edge(N - (i << 1),N - i,val[N - (i << 1)]),
		add_edge(N - (i << 1 | 1),N - i,val[N - (i << 1 | 1)]),
		add_edge(i,i << 1,val[i << 1]), add_edge(i,i << 1 | 1,val[i << 1 | 1]);
	
	for(ans = 0;ans < 0x3f3f3f3f && BFS();) ans += dinic(1,0x3f3f3f3f);
	ans = min(ans,min(val[1],val[N - 1])),
	printf("%d\n",ans >= 0x3f3f3f3f ? -1 : ans);
	
	fclose(stdin), fclose(stdout);
	return 0;
}

我表示别学他。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值