zoukankan      html  css  js  c++  java
  • ZJOI2020训练题2

    T1 做任务

    题目描述

    现有一款游戏,你作为玩家,拥有k种物品。开始时,每种物品有1000件。

    现在,在你面前有n个任务,每种任务都可能消耗一些物品,也可能得到一些物品。做第i个任务的物品得失情况用一个包含k个字母的字符串Si表示,其中每个字母都是+,-,/中的一种,第j个字母表示该任务对物品j的数量的影响。+表示做这个任务能得到一个物品j,-表示做这个任务会消耗一个物品j,/表示做这个任务对物品j的数量不产生影响。

    但是,做任务是有前提条件的。游戏设计者约定了m个限制关系,每个限制关系是一个有序数对(i, j),表示做任务i前必须先做任务j。

    现在,你需要选择性的做一些任务,使得获得的物品最多。

    比较两种方案的获得的物品数的方法如下:先比较他们获得第物品1的个数,若相同,再比较物品2,若相同,再比较物品3,以此类推。

    Input

    第一行有三个整数n,m,k

    接下来的n行,每行有一个字符串,第i行的字符串表示Si

    接下来的m行,每行有两个整数i,j表示限制关系

    Output

    输出包含一行,k个空格分隔的整数,表示做完你选的任务之后每种物品的拥有量。

    Sample Input

    3 3 2

    +-

    +/

    -+

    2 1

    2 3

    3 1

    Sample Output

    1001 1000

    Constraints

    有20%的数据,n ≤ 10, k = 1

    另有20%的数据,m = 0。

    另有20%的数据,限制关系是一条链(m = n-1且所有i各不相同,所有j各不相同)。

    另有20%的数据,限制关系是一棵树(m = n-1且所有i各不相同)。

    100%的数据,n≤1000, k≤5, m≤5000,保证限制关系不会有环。

    时间限制:1S

    内存限制:512M

    考试时的思路

    分类讨论,前80pts

    1. n <= 10 二进制枚举暴力
    2. m = 0 无限制条件,直接贪心选第一位为+的,然后在第一位不变的情况下选第二位为+的,依次找下去,当然也可以排序
    3. m = n - 1 当成是树来做,将没有被限制的点作为根,然后记dp[i]表示以i为根子树中选一些方案的最优解,每次先递归处理子树,然后考虑要不要加入这棵子树,最后再和全0做一次比较

    以上为80pts的思路

    [Code]

    #include<bits/stdc++.h>
    using namespace std;
    #define N 2050
    #define M 10050
    
    int read(){
    	int x=0; char c=getchar(); int flag=1;
    	while(!isdigit(c)) { if(c=='-') flag=-1; c=getchar(); }
    	while(isdigit(c)) { x=((x+(x<<2))<<1)+(c^48); c=getchar(); }
    	return x*flag;
    }
    
    int n,m,k;
    int a[N][6];
    struct Edge{
        int to,next;
    }edge[M];
    struct EDGE{
        int u,v;
        bool tag;
    }e[M];
    int head[N];
    int tmp;
    int rd[N];
    void add(int x,int y){
        ++ tmp; ++ rd[y]; edge[tmp].to = y; edge[tmp].next = head[x]; head[x] = tmp;
        e[tmp].u = x; e[tmp].v = y; e[tmp].tag = 0;
    }
    
    void Add(int x,int y){
        ++ tmp; ++ rd[y]; edge[tmp].to = y; edge[tmp].next = head[x]; head[x] = tmp;
    }
    
    namespace Task1{
    	int ans[6],tmp[6];
    	
    	void work(){
    	    for(int i = 1; i <= k; ++ i) ans[i] = 1000;
    	    
    		for(int i = 0; i < (1 << n); ++ i){
    			bool flag = 1;
    		    for(int j = 1; j <= n; ++ j){
    			    if(!(i & (1 << (j - 1)))){
    				    for(int p = head[j]; p != -1; p = edge[p].next){
    					    int t=edge[p].to;
    					    if(i & (1 << (t - 1))) { flag = 0; break ;}
    					} 
    				}
    				if(!flag) break;
    			}
    			if(!flag) continue;
    			
    			//printf("OK = %d
    ",i);
    			for(int j = 1; j <= k; ++ j) tmp[j] = 1000;
    			for(int j = 1; j <= n; ++ j) if(i & (1 << (j - 1)) ) { for(int p = 1; p <= k; ++ p) tmp[p] += a[j][p]; } 
    			
    			flag = 0;
    			for(int p = 1; p <= k; ++ p)
    			if(tmp[p] > ans[p]) { flag = 1; break; }
    			else if(tmp[p] < ans[p]) break;
    			
    			if(flag){
    				//printf("MORE %d
    ",i); 
    			    for(int p = 1; p <= k; ++ p) ans[p] = tmp[p];
    			}
    		}
    		
    		for(int i = 1; i <= k; ++ i) printf("%d ",ans[i]);
    	    puts("");
    	}
    }
    
    namespace Task2{
    	int ans[6];
    	
    	void work(){
    	    for(int i = 1; i <= k; ++ i) ans[i] = 1000;
    		for(int i = 1; i <= n; ++ i){
    		    bool flag = 1;
    			for(int j = 1; j <= k; ++ j){
    			    if(a[i][j] == 0) continue;
    			    if(a[i][j] < 0) flag = 0;
    			    break;
    			}
    			
    			if(flag){
    			    for(int j = 1; j <= k; ++ j) ans[j] += a[i][j];
    			}
    		}
    		for(int i = 1; i <= k; ++ i) printf("%d ",ans[i]);    
    	    puts("");
    	}
    }
    
    namespace Task3{
    	int g[N][6];
    	
    	void dfs(int nod){
    	    for(int i = 1; i <= k; ++ i) g[nod][i] = a[nod][i];
    	    
    	    for(int i = head[nod]; i != -1; i = edge[i].next){
    		    int t = edge[i].to;
    		    dfs(t);
    		    
    		    bool flag = 1;
    		    for(int j = 1; j <= k; ++ j) {
    			    if(g[t][j] == 0) continue;
    			    if(g[t][j] < 0) flag = 0;
    			    break;
    			}
    			
    			if(flag) { for(int j = 1; j <= k; ++ j) g[nod][j] += g[t][j]; }
    		}
    		
    		bool flag = 1;
    		for(int i = 1; i <= k; ++ i){
    		    if(g[nod][i] == 0) continue;
    		    if(g[nod][i] < 0) flag = 0;
    		    break;
    		}
    		if(!flag) { for(int i = 1; i <= k; ++ i) g[nod][i] = 0; }
    	}
    	
    	void work(){
    		int root;
    		for(int i = 1; i <= n; ++ i) if(!rd[i]) { root = i; break; }
    
    	    dfs(root);
            
            for(int i = 1; i <= k; ++ i) printf("%d ",g[root][i] + 1000);
            puts("");
    	}
    }
    
    signed main(){
    	memset(head, -1, sizeof(head));
        n = read(), m = read(), k = read();
        for(int i = 1; i <= n; ++ i){
    	    char s[10];
    	    scanf("%s",s);
    	    for(int j = 1; j <= k; ++ j)
    	    if(s[j - 1] == '+') a[i][j] = 1;
    		else if(s[j - 1] == '-') a[i][j] = -1; 
    	}
    	for(int i = 1; i <= m; ++ i){
    	    int x = read(), y = read();
    	    add(y,x); 
    	}
    	
    	if(n <= 10) { Task1::work(); return 0; }
    	if(m == 0) { Task2::work(); return 0; }
    	if(m == n - 1) { Task3::work(); return 0; }
        return 0;
    }
    //Still WA 
    

    正解

    考虑一个任务的奖励很像k位数,所以将其转化成10进制数,然后权值有正有负,考虑对于限制关系建图,方式就是如果做任务i之前需要做任务j,那就从i向j连一条边,然后对于这个图求最大权闭合子图.选出的子图就是答案.

    首先最大权闭合子图是用所有正权值之和减去原图的最小割,然后我们相对于限制反向建边,就是要求如果要选一个点,则它之前的所有点必须要选.这样就满足了限制.

  • 相关阅读:
    android IntentService生命周期问题
    日志
    python for android : BeautifulSoup 有 bug
    光电耦合器简单介绍以及作用
    cocos2dx 3.1从零学习(五)——动画
    openssl之EVP系列之9---EVP_Digest系列函数的一个样例
    html5 SVG
    CSS选择器
    ISCC2014-reverse
    哇塞!HTML5 实现的雨滴效果 CSS发抖
  • 原文地址:https://www.cnblogs.com/zzhzzh123/p/12253732.html
Copyright © 2011-2022 走看看