WC 2008 password

本文介绍了一道关于欧拉回路的题目,通过构建特定的图模型,并利用最小费用流算法解决该问题。文章详细阐述了如何划分顶点类别、如何连接边及其费用,并给出了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

很好的一道题。。。抽出模型是这题关键的一步。。。其实可以作为传统类题的。。。但是如果有任何关于连通性的描述就会彻底破坏这题的价值。。。所以它就成了提交答案式。。。

我只做了欧拉回路的测试点。。。因为木有时间了。。。构图很简单。。。将入度大于出度的作为A类,入度小于出度的为B类。。。源向A连边,流量为in-out,B向汇连边,流量为out-in。。。中间的为oo,费用分情况讨论。。。(abc)->(bcd)为1,(abc)->(cde)为2,(abc)->(def)为3。。。

最后输出方案时注意,由于你求的是回路。。。那么首尾点一定是同一个点。。。对于倒数第二个点。。。有可能会与第一个点有重复的地方,也要去掉。。。

Code:

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
const int source=1000,sink=1001,maxm=2020055;
typedef long long ll;
int e=1,ed=1,top=0,tail=0,m=0,len=0,tot=0,totlen=0;
int head[1005],now[1005],link[maxm],next[maxm],cost[maxm],dis[1005],sta[maxm*3],pre[1005],list[maxm],ans[maxm];
ll w[maxm],d1[1005],d2[1005],g[1005][1005];
bool vis[1005];
bool spfa()
{
  top=0;
  sta[tail=1]=source;
  memset(vis,0,sizeof(vis));
  memset(dis,65,sizeof(dis));
  memset(pre,0,sizeof(pre));
  int oo=dis[0]-1,x=0,y=0,ne=0;
  dis[source]=0;
  vis[source]=1;
  while (top<tail)
    {
      x=sta[++top];
      for (ne=head[x],y=link[ne];ne;ne=next[ne],y=link[ne])
	if (w[ne])
	  if (dis[x]+cost[ne]<dis[y])
	    {
	      dis[y]=dis[x]+cost[ne];
	      if (!vis[y])
		sta[++tail]=y,vis[y]=1;
	      pre[y]=x;
	      now[y]=ne;
	    }
      vis[x]=0;
    }
  if (dis[sink]<oo) return 1;
  return 0;
}
void add(int u,int v,ll tmpw,int tmpc)
{
  next[++e]=head[u];
  head[u]=e;
  link[e]=v;
  w[e]=tmpw;
  cost[e]=tmpc;
  next[++e]=head[v];
  head[v]=e;
  link[e]=u;
  w[e]=0;
  cost[e]=-tmpc;
}
int get()
{
  int nop=sink;
  ll flows=2000000000;
  for (;nop!=source;nop=pre[nop])
    if (flows>w[now[nop]])
      flows=w[now[nop]];
  for (nop=sink;nop!=source;nop=pre[nop])
    w[now[nop]]-=flows,w[now[nop]^1]+=flows;
  return flows*dis[sink];
}
void dfs(int i)
{
  for (int j=0;j<=999;j++)
    if (g[i][j])
      {
	g[i][j]--;
	dfs(j);
      }
  list[++len]=i;
}
int main(int argc,char *argv[])
{
  char inname[200],outname[200];
  memset(inname,0,sizeof(inname));
  memset(outname,0,sizeof(outname));
  sprintf(inname,"password%s.in",argv[1]);
  sprintf(outname,"password%s.out",argv[1]);
  freopen(inname,"r",stdin);
  freopen(outname,"w",stdout);
  scanf("%d",&m);
  int i=0,j=0;
  int ch=0,pres=0,nows=0;
  for (i=1;i<=m;i++)
    {
      scanf("%d",&ch);
      pres=ch/10;
      nows=ch%1000;
      if (!g[pres][nows])
	{
	  g[pres][nows]=1;
	  d1[pres]++;
	  d2[nows]++;
	  totlen++;
	}
    }
  int tmpc=0;
  for (i=0;i<=999;i++)
    if (d1[i]>d2[i])
      for (j=0;j<=999;j++)
	if (d2[j]>d1[j])
	  {
	    if ((j%100)==(i/10))
	      tmpc=1;
	    else
	      if ((j%10)==(i/100))
		tmpc=2;
	      else
		tmpc=3;
	    add(j,i,2000000000,tmpc);
	  }
  for (i=0;i<=999;i++)
    if (d2[i]>d1[i])
      add(source,i,d2[i]-d1[i],0);
    else
      if (d1[i]>d2[i])
	add(i,sink,d1[i]-d2[i],0);
  while (spfa()) totlen+=get();
  int ne=0;
  for (i=0;i<=999;i++)
    if (d2[i]>d1[i])
      for (ne=head[i];ne;ne=next[ne])
	if (!(ne&1))
	  g[i][link[ne]]+=w[ne^1];
  len=0;
  for (i=0;i<=999;i++)
    if (d1[i]+d2[i]>0)
      {
	dfs(i);
	break;
      }
  len--;
  tot=3;
  ans[1]=list[len]/100;
  ans[2]=(list[len]/10)%10;
  ans[3]=list[len]%10;
  for (i=len-1;i>=1;i--)
    { 
      if ((list[i+1]%100)==(list[i]/10))
	{
	  tot++;
	  ans[tot]=list[i]%10;
	}
      else
	if ((list[i+1]%10)==(list[i]/100))
	  {
	    tot+=2;
	    ans[tot-1]=(list[i]/10)%10;
	    ans[tot]=list[i]%10;
	  }
	else
	  {
	    tot+=3;
	    ans[tot-2]=list[i]/100;
	    ans[tot-1]=(list[i]/10)%10;
	    ans[tot]=list[i]%10;
	  }
    }
  printf("%d\n",totlen);
  for (i=1;i<=totlen;i++)
    printf("%d",ans[i]);
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值