zoukankan      html  css  js  c++  java
  • The Summer Training Summary(1)-- the second

    The Summer Training Summary-- the second

    - 拓扑排序 hdu 1285

    问题分析 怎么说呢 拓扑排序 给我的感觉就是 一棵树 从树冠 往下
    每次遇到 比他的小的 就把它的入度+1 就往树下 走 然后从树冠 找
    入度为0 是就是一个 小树冠 其下拥有若干 小树 小分支的入度为0 意味着到了
    小树冠的底部但着并不意味着 这就是最小值 因为 可能还有比这个数的最大入度
    还大的数存在

    为了满足 不确定的 名次按照 队伍字典序排序
    借助于优先队列来实现这个; 首先将 起始入度为0 的队伍加入 这意味着 树冠 (他们的 名词相对来说是靠前的单不能确定他们的具体名词 优先队列巴拉巴拉...)
    然后 出队 扫描 分支 入度减一 存在 入度为0 就继续加入优先队列
    具体代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define mem(a) memset(a,0,sizeof(a))
    using namespace std;
    int n,m,map[505][505],in[505];
    int main()
    {
    	while(cin>>n>>m)
        {
        	mem(map);
    		mem(in);
    		while(m--)
    		{
    			int a,b;
    			cin>>a>>b;
    			if(!map[a][b])
    			{
    				map[a][b]=1;
    			in[b]++;
    			}
    			
    		 } 
    	  priority_queue<int,vector<int>,greater<int> > q;
    	  for(int i=1;i<=n;i++)
    	  {
    	  	if(!in[i])
    	      q.push(i);
    	  }
    	  bool f=0;
    	  while(q.size())
    	  {
    	  	int top=q.top();
    	  	q.pop();
    	  	in[top]--;
    	  	if(!f)
    		  cout<<top;
    		else
    		cout<<" "<<top;
    		f=1;
    		for(int i=1;i<=n;i++)
    		{
    			if(map[top][i])
    			{
    			in[i]--;
    			if(!in[i])
    		      q.push(i);	
    			}  
    		}
    	  }
    	  cout<<endl;
    	}
        return 0;
    }
    

    H - 拓扑排序 (2) hdu 3342

    问题分析 :
    和上个 拓扑排序一样只不过是是反向查询 是否是存在循环结构 如果 存在就意味这有错误
    而这在 程序中反映出来就是 查询值等于n;
    具体代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define mem(a) memset(a,0,sizeof(a))
    using namespace std;
    int n,m,map[505][505],in[505];
    int main()
    {
         while(cin>>n>>m&&n&&m)
    	 {     mem(map);
    		   mem(in);
    	 	for(int i=0;i<m;i++)
    		 {
    		 	int a,b;
    		 	cin>>a>>b;
    		 	if(!map[a][b])
    		  {
    		  	map[a][b]=1;
    		  	in[b]++;
    		  }
    		  }
    		   
    		  int flag=0;
    		  int i,j;
    		  for( i=0;i<n;i++)
    		  {
    		  	for( j=0;j<n;j++)
    		  	{
    		  		if(!in[j])
    		  		{
    		  			in[j]--;
    		  			for(int k=0;k<n;k++)
    		  			{
    		  				if(map[j][k])
    					     in[k]--;
    					}
    					break;
    				  }
    			  }
    			  if(j==n)
    			  {
    			  	flag=1;
    			  break;	
    			  } 
    		  }
    		  if(flag)
    		  cout<<"NO
    ";
    		  else
    		  cout<<"YES
    ";
    		  
    	   }  
        return 0;
    }
    

    L - 拓扑排序 HDU 2647

    就是拓扑排序 中间 加了一个 一888作为基值
    代码如下:

    #include<iostream>
    #include<cstring>
    #include<queue>
    #include<vector>
    #define mem(a)  memset(a,0,sizeof(a))
    using namespace std;
    int main()
    {
     int n,m;
     while(cin>>n>>m)
    {
     vector<int> map[20001];
     int in[10001],mo[10001];
     for(int i=1;i<=n;i++)
     map[i].clear();
     mem(in);
     queue<int> q;
     while(m--)
     {
     	int a,b;
     	cin>>a>>b;
     	map[b].push_back(a);
     	in[a]++;	   
     }
     for(int i=1;i<=n;i++)
        if(!in[i])
        {
        	q.push(i);
        	mo[i]=888;
        }
        
        int tot=0;
        int sum=0;
        while(!q.empty())
        {
        	int x=q.front();
        	sum+=mo[x];
        	tot++;
        	q.pop();
        	for(int i=0;i<map[x].size();i++)
     	   {
        		in[map[x][i]]--;
        		mo[map[x][i]]=mo[x]+1;
        		if(!in[map[x][i]])
        		q.push(map[x][i]);
     	   }    	   
        }
        if(tot<n)
        cout<<-1<<endl;
        else
        cout<<sum<<endl;	   
     }
     return 0;
    }
    

    I - 并查集 poj 1456

    问题分析:
    这题有多种解法 我用是结构体 加优先队列 (结构体可以换成pair)
    建立 天数 与 价格的结构体
    然后按照 价格进行排序
    开一个 cnt记录 所在天数(每天限制卖1中水果)
    从头扫到尾部 在这期间
    因为可能存在同一天但是确有多种选择
    保持cnt<=结构体的.day
    如果大于了就 检查 队首是否小于 新的 小就入队

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define mem(a) memset(a,0,sizeof(a))
    using namespace std;
    struct node
    {
    	int vla;
    	int d;
    }ans[10005];
    int cmp(node a,node b)
    {
    	return a.d<b.d;
    }
    int main()
    { 
    int n;
    while(cin>>n)
    {
    	for(int i=0;i<n;i++)
    		cin>>ans[i].vla>>ans[i].d;
       sort(ans,ans+n,cmp);
       long long sum=0;
       int cnt=1;
       priority_queue<int,vector<int>,greater<int> >q;
       for(int i=0;i<n;i++)
       {
       	   if(cnt<=ans[i].d)
       	   {
       	   	q.push(ans[i].vla);
       	   	cnt++;
    		  }
    		  else if(q.top()<ans[i].vla)
    		  {
    		  	q.pop();
    		  	q.push(ans[i].vla);
    		  }
       }
       while(q.size())
       {
       	sum+=q.top();
       	q.pop();
       }
       cout<<sum<<endl;
    }
        return 0;
    }
    

    J - 并查集 poj 1611

    问题分析:并查集模板 直接套 然后求0点下的子节点个数
    代码如下:

    #include<iostream>
    #include<cstring>
    #include<queue>
    #include<vector>
    #define mem(a)  memset(a,0,sizeof(a))
    using namespace std;
    int sum[30005],r[30005];
    int find(int a)
    {
    	if(r[a]!=a)
    	r[a]=find(r[a]);
    	return r[a];
    }
    int build(int a,int b)
    {
    	int x=find(a);
    	int y=find(b);	
    	if(x!=y)
    	{
    		r[y]=x;
    		sum[x]+=sum[y];
    	}
    	return 0;
    }
    int main()
    {
     int n,m;
     while(cin>>n>>m&&n)
        {
        	for(int i=0;i<n;i++)
        	{
        		sum[i]=1;
        		r[i]=i;
    		}
    		while(m--)
    		{
    			int t,a,b;
    			cin>>t>>a;
    			t--;
    			while(t--)
    			{
    				cin>>b;
    				build(a,b);
    			} 
    		}
    		cout<<sum[find(0)]<<endl;
    	}	
    	return 0;
    }
    

    M - 带权并查集 hdu 3038D

    问题分析:
    带权并查集 核心就是更新权值
    主要 公式 寻根时的更新: val[a]+=val[pr[a]];
    建立新链接是的更新 val[find(b)]=val[a]+v-val[b];
    同根时检查 是否符合权值
    代码如下:

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #define  mem(a,b) memset(a,b,sizeof(a))
    using namespace std;
    const int maxn =200005;
    int  val[maxn],pr[maxn];
    int find(int a)
    {
       if(pr[a]==-1)
       return a;
       int t=find(pr[a]);
        val[a]+=val[pr[a]];
        return pr[a]=t;
    }
    int main() 
    {
       int n,m,ans=0;
       while(cin>>n>>m)
       {
       	ans=0;
       	mem(val,0);
    mem(pr,-1);
    while(m--)
    {
       int a,b,v;
       cin>>a>>b>>v;
       a--;
       int x=find(a);
       int y=find(b);
       if(x!=y)
       {
       	pr[y]=x;
       	val[y]=val[a]-val[b]+v;
        } 
        else if( val[b]-val[a]!=v)
         ans++;
    }
    cout<<ans<<endl;
       }
    
       return 0;
    }
    

    N - 种类并查集 POJ 2492

    .种类并查集有2中写法:
    1:一种是开倍数数组记记录 检查是否冲突 不冲突就合并(双向)
    2:还有一种 是依赖于一个 偏移向量数组(相当于 权值数组 大神起的名字一听就感觉霸气) 来区别种类
    这个题 用了 第一种方法 并 配了 测试数据
    感觉关键就是 a-b+maxn b-a+maxn建立关联但他们相对 性别来说又是相对独立的
    一旦出现 相同性别sex的时候就会找到同一个根
    测试输出 各个数字 含义标式:

    起始值 -起始值根植-过程根植-尾根植(就是root[a]=a情况 值a/root[a])

    1000 4 4
    1 2
    a-again        1 1000002 1000002     a-end
    b-maxn|-again: 2 2     b-end
    b-again        1000002 1000002      b-end
    
    a-again        2 1000001 1000001     a-end
    b-maxn|-again: 1 1000002 1000002     b-end
    b-again        1000001 1000001      b-end
    
    2 3
    a-again        2 1000001 1000003 1000003     a-end
    b-maxn|-again: 3 3     b-end
    b-again        1000003 1000003      b-end
    
    a-again        3 1000002 1000002     a-end
    b-maxn|-again: 2 1000003 1000003     b-end
    b-again        1000002 1000002      b-end
    
    1 4
    a-again        1 1000002 1000004 1000004     a-end
    b-maxn|-again: 4 4     b-end
    b-again        1000004 1000004      b-end
    
    a-again        4 1000003 1000003     a-end
    b-maxn|-again: 1 1000004 1000004     b-end
    b-again        1000001 1000003 1000003      b-end
    
    1 3
    1000004 Scenario #1:
    Suspicious bugs found!
    

    最好是自己推理一遍,有点难以理解..我是看了好久(看自闭的呢种 0_0 .... )
    输入 必须要用扫描 scanf() 要不就tle了...

    代码如下:(含测试部分)

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include<cstdio>
    #define  mem(a,b) memset(a,b,sizeof(a))
    using namespace std;
    const int maxn =1000000;
    int  p[maxn*2],flag;
    int find(int a)
    {
        if( p[a]!=a)
        {
        	p[a]=find(p[a]);
        	cout<<p[a]<<" ";
    	}
          
        return p[a];
    }
    int finds(int a)
    {
        if( p[a]!=a)
        {
        	p[a]=find(p[a]);
    	}
          
        return p[a];
    }
    int build(int a,int b)
    {
    	  int x,y;
    	  x=finds(a);
    	  y=finds(b-maxn);
    	  if(x==y)
    	  {
    	  	flag=0;
    	  	return 0;
    	  }
    	  y=finds(b);
    	  if(x!=y)
    	  {
    	  	p[x]=p[y];
    	  }
    	  /*
    	  cout<<"a-again        "<<a<<" "<<p[a]<<" ";
    	  x=find(a);
    	  cout<<"    a-end"<<endl;
    	  cout<<"b-maxn|-again: "<<b-maxn<<" "<<p[b-maxn]<<" ";
    	  y=find(b-maxn);
    	  cout<<"    b-end"<<endl;
    	  cout<<"b-again        "<<b<<" "<<p[b]<<" ";
    	  y=find(b);
    	  cout<<"     b-end"<<endl<<endl;
    	  */	
    }
    int main() 
    {
        int t,n,m,ans=0;
        scanf("%d",&t);
       for(int i=0;i<t;i++)
        {
    	scanf("%d%d",&n,&m);
        	ans++;
        	for(int i=1;i<=n;i++)
        	{
        		p[i]=i;
    			p[i+maxn]=maxn+i; 
    		} 
        	flag=1;
          while(m--)
          {
          	int a,b;
          	scanf("%d%d",&a,&b);
          	build(a,b+maxn);
          	build(b,a+maxn);
          	} 
          	printf("Scenario #%d:
    ",ans);
          	if(flag)
          	printf("No suspicious bugs found!
    ");
          	else
          	printf("Suspicious bugs found!
    ");
          	cout<<endl;
         }
        return 0;
    }
    

    O - 种类并查集 POJ 1182

    这个用就是 变量val[i] 定值 区分
    这个 玩意推了好久才看明白
    附上大神 全面解析
    把大神的解析精简了一下面结合起来应该会更好!(这里用 val代替relation).
    1.偏移数组 val[]值的含义
    是由于 输入关系1 2决定(取用 输入的关系-1) 因此
    规定 0:同类 1:子被吃 x->y 2: 子吃父x<-y (这样子就可以 用%3进行 压缩)
    2.路径压缩算法 公式
    x->y->z (子->父->爷)关系表达式(枚举推理,x->y代表 x是y 的根哈~)
    val[爷]=(vla[子]+val[父])%3 (%3为了 防止大于等于3的情况出现 )
    同时 可以得到 x->y(子被吃 1)=y<-x(子吃父3-2)
    可以得到 x->y(子被吃 3-1)=y<-x(子吃父2)
    3.集合间情关系 确定公式:
    val[b]=(3- val[y]+(relation -1)+val[x])%3;(relation 缩写 r)
    (b=find(y),b为y的根,输入:relation x y )

    关系图
    X------>Y<-------B
    r-1 val[y]
    X------>Y------->B
    r-1 3-val[y]
    A----->-X------>Y------->B
    val[x] r-1 val[y]

    四. 判定公式

    据题意共计4种为假话
    1. x>n或者y>n
    2. r=2&&x==y
    3.a!=b时
    1) x==&&val[x]!=val[y]
    2) x==2&&(3-val[x]+val[y])%3!=1

    关于3-2)的图解

    目前状态 过程 所求状态
    A<---X---->Y 变下方向:3-val[x] A--->X---->Y

    累.......总算写完了哭泣~~
    代码如下:

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include<cstdio>
    #define  mem(a,b) memset(a,b,sizeof(a))
    using namespace std;
    const int maxn =50010;
    int  p[maxn],val[maxn];
    int find(int a)
    {
        int t;
        if(a==p[a])
        return a;
        t=p[a];
        p[a]=find(t);
        val[a]=(val[a]+val[t])%3;
        return p[a];
    }
    int main() 
    {
         int n,m,ans=0;
    scanf("%d%d",&n,&m);
    	 for(int i=0;i<=n;i++)
         {
         	p[i]=i;
         	val[i]=0;
    	 }
       while(m--)
       {
       	int relation,x,y;
    scanf("%d%d%d",&relation,&x,&y);
       	if(x>n||y>n)
       	ans++;	
       	else if(relation==2&&x==y)
       		ans++;
       	else
       	{
       		int a=find(x),b=find(y);
       		if(a!=b)
       		{
       			p[b]=a;
       			val[b]=(3+relation-1+val[x]-val[y])%3;
    		   }
    		   else
    		   {
    		   	if(relation==1&&val[x]!=val[y])
    		   	ans++;
    		   	if(relation==2&&((3-val[x]+val[y])%3)!=1)
    		   	ans++;
    		   }
    	}
       }
       printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    java之大文件断点续传
    Html5大文件断点续传
    前端js怎么实现大文件G级的断点续传(分块上传)和分段下载
    HTML5解决大文件断点续传
    完整版断点续传、秒传,支持超大大大文件_支持重定义文件名和路径
    起来吧!不要做奴隶的ITproject师们!
    Oracle PL/SQL 编程基础 实例
    C语言高速入门系列(八)
    POJ 3253-Fence Repair(堆)
    抽屉式导航可能减少产品一半的用户參与度
  • 原文地址:https://www.cnblogs.com/maxv/p/11231372.html
Copyright © 2011-2022 走看看