ICPC2017 Hua-Lian(solve9/12)

A

题意:给你n个数,问你改变一个数是否可以保证序列中任意相邻的差值的绝对值小于等于m

思想:当前序列有1、2个数肯定没问题,如果第一个或者最后一个跟前边不满足也可以搞一下,对于中间不行的话,如果只有一个的话,考虑下2种情况,一种是当前放在中间,一种是最后,有一个满足即可,如果2个数的话,必须是前后挨着,然后考虑下是否差值<=2*m。

B

题意:给你n*(n-1)/2个关系,表示i和j的亲密度,问你其中一个三元组的亲密度最大。

思想:n^3暴力

C

题意:给你n,m,k.问你C(n,m)的值转化成K进制是多少。

思想:直接无脑java打表,然后tostring即可。

D题

题意:给你n条路,每条路边权都是1,问你最短路径的值不同的个数。就是任意2点最短路是X有多少条,输出下。

思想:Floyd跑一下,输出下,刚开始数组的时候1<<29即可,如果1<<30 相加会爆掉

#include<bits/stdc++.h>
using namespace std;
int Map[250][250];
int sum[250];
int main()
{
	int n,m;
	while(scanf("%d",&n)!=EOF)
	{
		if(n==0)
			break;
		scanf("%d",&m);
		int a,b;
		for(int i=0;i<=n;i++)
			for(int j=0;j<=n;j++)
				Map[i][j]=(1<<29);
		memset(sum,0,sizeof(sum));
		for(int i=0;i<m;i++)
		{
			scanf("%d%d",&a,&b);
			a++;
			b++;
			Map[a][b]=Map[b][a]=1;
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				for(int k=1;k<=n;k++)
				{
					if(Map[j][k]>Map[j][i]+Map[i][k])
					{
						Map[j][k]=Map[j][i]+Map[i][k];
					}
				}
			} 
		} 
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(i!=j && Map[i][j]!=(1<<29))
					sum[Map[i][j]]++;
			}
		}
		for(int i=1;i<=n;i++)
		{
			if(sum[i])
				printf("%d %d\n",i,sum[i]);
		}
	}
	return 0;
} 

E

题意:给你一个图,然后告诉你你的城堡和坏人的城堡,然后告诉你坏人的兵力,而且你的兵力可以1个打三个,问你最少安排多少兵可以让你的城堡绝对安全,最小割的板子题,最小割*(k+2)/3即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010;
const int INF=0x3f3f3f3f;
struct node{
    int t,cap,flow,next;    //cap容量,flow流量
}e[N*N*2];
int head[N],cur[N],cnt;  //cur优化dfs中的head
int d[N];   			 //bfs深度
void init(int n)
{
    cnt=0;
    memset(head,-1,sizeof(head[0])*(n+1));
}
void add(int u,int v,int cap)   //u->v容量为cap
{
    e[cnt]=node{v,cap,0,head[u]};
    head[u]=cnt++;
    e[cnt]=node{u,0,0,head[v]};   //容量为0的反向边
    head[v]=cnt++;
}
bool bfs(int s,int t)   //O(n+m)
{
    memset(d,0,sizeof(d));
    queue<int>q;
    q.push(s);
    d[s]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];~i;i=e[i].next)
        {
            int v=e[i].t;
            if(d[v]==0&&e[i].cap-e[i].flow>0)
            {
                d[v]=d[u]+1;
                q.push(v);
            }
        }
    }
    return d[t]>0;     //存在增广路
}
int dfs(int s,int t,int minedge)
{
    if(s==t)
		return minedge;
    int flow=0;    //从当前s点流出的流量
    for(int &i=cur[s];~i;i=e[i].next)
    {
        int v=e[i].t;
        if(d[v]==d[s]+1&&e[i].cap-e[i].flow>0)   //层次关系&&有剩余流量
        {
            ll temp=dfs(v,t,min(minedge-flow,e[i].cap-e[i].flow));
            e[i].flow+=temp;    //流量增加
            e[i^1].flow-=temp;    //反向边流量减少
            flow+=temp;    //flow已分配的流量
            if(flow==minedge)return flow;  //已达到祖先的最大流,无法再大,剪枝
        }
    }
    if(flow==0)
		d[s]=0;   //此点已无流,标记掉
    return flow;
}
ll dinic(int s,int t)   //一定要建立反向边cap=0
{
    ll maxflow=0;
    while(bfs(s,t))   //有增广路
    {
        memcpy(cur,head,sizeof(head));   //重要的优化
        maxflow+=dfs(s,t,INF);
    }
    return maxflow;
}
int main()
{
    int T,n,m,s,t,k,u,v;
    cin>>T;
    while(T--)
    {
    	scanf("%d%d%d%d%d",&n,&m,&s,&t,&k);
        init(n);
        while(m--)
        {
        	scanf("%d%d",&u,&v);
            add(u,v,1);
            add(v,u,1);
        }
        ll ans=dinic(s,t);
        ans=(k+2)/3 * ans;
        printf("%lld\n",ans);
    }
}

H

题意:给你一个数,问你构建出来如图的结构有多少条边。

思想:通过图推出来对于一个数来说,每次连的边都是一个本身除去一个素数。模拟下过程即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<long long>V;
map<long long,int>M;
void div(ll n)
{
	for(ll i=2;i*i<=n;i++)
	{
		if(n%i==0)
		{
			V.push_back(i);
			while(n%i==0)
			{
				n=n/i;
			}
		}
	}
	if(n>1)
		V.push_back(n);
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		ll temp;
		int ans=0;
		scanf("%lld",&temp);
		V.clear();
		M.clear(); 
		div(temp);
		queue<long long>q;
		q.push(temp);
		while(!q.empty())
		{
			temp=q.front();
			q.pop();
			for(int k=0;k<V.size();k++)
			{
				if(temp%V[k]==0)
				{
					if(M[temp/V[k]]==0)
					{
						M[temp/V[k]]=1;
						q.push(temp/V[k]);
					}
					ans++;
				}
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

I题

题意:给你n个左边的m个右边的,然后给你k对关系,然后问你选择x个点,让他无法攻击到2个点以上。

思想:二分图最大匹配

#include<bits/stdc++.h>
using namespace std;
const int maxn=100;
int pre[maxn];
int vis[maxn];
int Map[maxn][maxn];
int n,m,k; 
int check(int i)
{
    for(int j=0;j<n;j++)
    {
        if(vis[j]==0 && Map[i][j])
        {
            vis[j]=1;
            if(pre[j]==-1 || check(pre[j]))
            {
                pre[j]=i;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    while(scanf("%d",&m)!=EOF)
    {
        if(m==0)
            break;
        scanf("%d%d",&n,&k);
        memset(pre,-1,sizeof(pre));
        memset(Map,0,sizeof(Map));
        for(int i=0;i<k;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            Map[a][b]=1; 
        }
        int ans=0;
        for(int i=0;i<m;i++)//m匹配n 
        {
            memset(vis,0,sizeof(vis));
            if(check(i))
                ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

K题

题意:问你是否可以可以将整个图染成三种颜色,而且相邻两点颜色不同

思想:dfs爆搜,剪枝下。默认0是一个颜色,然后考虑其他的,然后每层比对下挨着的是否颜色一样。

#include<bits/stdc++.h>
using namespace std;

int vis[30][30];
int Color[30];
int n,m;
int dfs(int u)
{
	for(int i=0;i<u;i++)
		if(vis[i][u] && Color[i]==Color[u])
			return 0;
	if(u==n-1)
		return 1;
	for(int i=0;i<3;i++)
	{
		Color[u+1]=i;
		if(dfs(u+1))
			return 1;
	}
	return 0;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		memset(Color,0,sizeof(Color));
		memset(vis,0,sizeof(vis));
		scanf("%d%d",&n,&m);
		for(int i=0;i<m;i++)	
		{
			int a,b;
			scanf("%d%d",&a,&b);
			vis[a][b]=vis[b][a]=1;
		}
		if(dfs(0))
			printf("Y\n");
		else	
			printf("N\n");
	}
	return 0;
}

L题

题意:给定一个字符串,通过切割成多个字符串提取循环节的方式压缩它,并使得提取出的循环节总长度最小。

思想:求区间的循环节的长度,dp[i]表示1-i最小的循环长度。分割整个字符串为一部分一部分,对于每部分求出来一个最短的循环节,然后拼成全部。

#include<bits/stdc++.h>
using namespace std;
char s[10005];
int dp[10005];
int Next[10005];
void get_next(char s[], int l) //next数组板子 
{
    for (int i = 2, j = 0; i <= l; i++) 
	{
        while (j > 0 && s[i] != s[j + 1]) 
			j = Next[j];
        if (s[i] == s[j + 1]) 
			j++;
        Next[i] = j;
    }
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%s",s+1);
		int len=strlen(s+1);
		for(int i=0;i<=len;i++)//初始化最大 
			dp[i]=i;
		for(int i=1;i<=len;i++)
		{
			get_next(s+i-1,len-i+1);//区间next更新 
			for(int j=i;j<=len;j++)
			{
				int temp=j-i+1;//len 
				if(temp%(temp-Next[temp])==0)//当前是循环节 
					dp[j]=min(dp[j],dp[i-1]+temp-Next[temp]); 
			}
		}
		printf("%d\n",dp[len]);
	}
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值