随笔 - 97, 文章 - 22, 评论 - 81, 引用 - 0
数据加载中……

PKU 3492 Knapsack II

题目链接:http://poj.org/problem?id=3492
/*
题意:
    给出n(n <= 500)个数ai(ai <= 5000),求他们的最大不能组合数。

解法:
数论 + 最短路

思路:
    经典的组合问题Change Making Problem,这个题目有一个限制就是给
定的数小于等于5000,题目的意思很清楚,就是求一个数S,使得它不能被
任何ai的倍数所组合出来,并且它的值最大。
    那么如果这n个数的gcd不为1,必然找不到这样的S,因为如果S不能被
它们的gcd整除,永远不可能组合出来,这样S就可以很大,自然也就没有最
大的S了。
    现在讨论所有ai的gcd为1的情况。对于任意的整数A,如果A能被以上的
ai组合出来,那么 B = A + k*a0 必然能被组合(只要加上k个a0即可)。于
是我们可以吧所有整数划分成a0个等价类,等价类中的数模a0的值相同,举
个例子,a0=3,我们可以将0,1,2,3,4,5,6划分成(0,3,6)(1,4)(2,5)这三个
等价类,如果相同等价类中的某个数能被组合,那么比它大的所有在该等价
类中的数必然能被组合出来。所以现在只要求出每个等价类中的最小的能被
组合出来的数,然后取所有等价类中最小数的最大值L,L-a[0]就是问题的
答案(原因很简单,因为L能被组合出来,比L大的并且在同一等价类中的数
必然能通过加上若干个a[0]来求得,于是L-a[0]就成了最大不能组合数)。
    于是问题就转化成了如何在相同等价类中找到最小的那个能被ai组合出
来的数。可以利用最短路来求,最短路的key信息就是它本身值的大小,如果
两个数x,y, (x + ai) % a0 == y % a0,那么我们就在x和y之前连上一条权
值为ai的边,构图完成后就可以从0这个点开始搜了,最后遍历0到a[0]-1找
到最大的那个数L即可。
*/


#include 
<iostream>
#include 
<algorithm>
#include 
<queue>
using namespace std;

#define maxn 5010
#define inf 200000000

int a[500];

struct Point {
    
int val;
    
int mod_val;
    
int dis;
    Point(
int _v, int _mv, int _d) {
        val 
= _v;
        mod_val 
= _mv;
        dis 
= _d;
    }

    friend 
bool operator < (Point a, Point b) {
        
return a.dis > b.dis;
    }

}
;

int dis[maxn], nv[maxn];
priority_queue 
< Point > Q;



int gcd(int a, int b) {
    
if(b == 0)
        
return a;
    
return gcd(b, a%b);
}


int main() {
    
int n;
    
int i;
    
while(scanf("%d"&n) != EOF) {
        
int G = 0;
        
for(i = 0; i < n; i++{
            scanf(
"%d"&a[i]);
            
if(!i)
                G 
= a[0];
            
else
                G 
= gcd(G, a[i]);
        }

        
if(G != 1{
            printf(
"INF\n");
            
continue;
        }


        sort(a, a 
+ n);
        
for(i = 0; i < a[0]; i++{
            dis[i] 
= inf;
        }

        dis[
0= 0;
        nv[
0= 0;
        
while(!Q.empty()) {
            Q.pop();
        }

        Q.push(Point(
000));

        
while(!Q.empty()) {
            Point id 
= Q.top();
            Q.pop();
            
for(i = 0; i < n; i++{
                
int nex = (id.mod_val + a[i]) % a[0];
                
if(id.dis + a[i] < dis[nex]) {
                    dis[nex] 
= id.dis + a[i];
                    nv[nex] 
= id.val + a[i];
                    Q.push(Point(nv[nex], nex, dis[nex]));
                }

            }

        }

        
int Max = 0;
        
for(i = 0; i < a[0]; i++{
            
if(dis[i] != inf && nv[i] > Max) {
                Max 
= nv[i];
            }

        }

        printf(
"%d\n", Max - a[0]);
    }

    
return 0;
}

posted on 2011-04-05 19:01 英雄哪里出来 阅读(1250) 评论(0)  编辑 收藏 引用 所属分类: 数学


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理