分类: OI路程

  • USACO 2.2 Subset Sums集合 解题报告

    USACO 2.2 Subset Sums集合
    For many sets of consecutive integers from 1 through N (1 <= N <= 39), one can partition the set into two sets whose sums are identical.
    For example, if N=3, one can partition the set {1, 2, 3} in one way so that the sums of both subsets are identical:
    {3} and {1,2}
    This counts as a single partitioning (i.e., reversing the order counts as the same partitioning and thus does not increase the count of partitions).
    If N=7, there are four ways to partition the set {1, 2, 3, … 7} so that each partition has the same sum:
    {1,6,7} and {2,3,4,5}
    {2,5,7} and {1,3,4,6}
    {3,4,7} and {1,2,5,6}
    {1,2,4,7} and {3,5,6}
    Given N, your program should print the number of ways a set containing the integers from 1 through N can be partitioned into two sets whose sums are identical. Print 0 if there are no such ways.
    Your program must calculate the answer, not look it up from a table.
    PROGRAM NAME: subset
    INPUT FORMAT
    The input file contains a single line with a single integer representing N, as above.
    SAMPLE INPUT (file subset.in)
    7
    OUTPUT FORMAT
    The output file contains a single line with a single integer that tells how many same-sum partitions can be made from the set {1, 2, …, N}. The output file should contain 0 if there are no ways to make a same-sum partition.
    SAMPLE OUTPUT (file subset.out)
    4
    Description
    对于从1到N (1 <= N <= 39) 的连续整数集合,能划分成两个子集合,且保证每个集合的数字和是相等的。举个例子,如果N=3,对于{1,2,3}能划分成两个子集合,他们每个的所有数字和是相等的:
    {3} and {1,2}
    这是唯一一种分法(交换集合位置被认为是同一种划分方案,因此不会增加划分方案总数)如果N=7,有四种方法能划分集合{1,2,3,4,5,6,7},每一种分发的子集合各数字和是相等的:
    {1,6,7} and {2,3,4,5} {注 1+6+7=2+3+4+5}
    {2,5,7} and {1,3,4,6}
    {3,4,7} and {1,2,5,6}
    {1,2,4,7} and {3,5,6}
    给出N,你的程序应该输出划分方案总数,如果不存在这样的划分方案,则输出0。程序不能预存结果直接输出。
    Input
    输入文件只有一行,且只有一个整数N
    Output
    输出划分方案总数,如果不存在则输出0。
    Sample Input
    7
    Sample Output
    4
    刚看到这题完全不知道怎么下手,就是枚举都不知道怎么下手的好,就直接看了看分析:n个数字的累加和为tot,只有当tot为偶数的时候题目才可能有解。一下就有点感觉了,在题目给出的例子中左边的集合的和等于右边的也等于整个集合{1…N}的和的一半,所以只有当tot为偶数的时候才能够有解。
    忽然感觉灵光爆满,知道怎么写了,可是仔细想一想,就是枚举我也还是不知道怎么下手的好啊。
    今天做出来了, 首先是自己做了一个枚举的, 提交测试一看, 果然第4个测试点超时, 后来看了网上的分析, 其实昨天我就看了分析, 只是没看懂而已, 后来自己的思考, 加上爸爸的一句话的指点, 就弄懂了, 自然而然的AC了.
    思路就是一个DP的思路, DP的公式就是:
    将数据{1…N}分成两个子集, f[i][j]就表示前i个数能够组合成和是j的个数. 最后要输出的也就是f[n][n*(n+1)/4].
    然后DP方程就是: f[i][j] = f[i – 1][j] + f[i – 1][j – i]
    意思就是说前i个数能够凑成和为j的个数是, 前i-1个数能够凑成和为j的数加上前i-1个数能够凑成和为j – i的个数. f[i – 1][j] 比较好理解, 但是f[i – 1][j – i]难理解一点, 后者的意思就是说前面i – 1个只需要凑成j – i就够了, 因为第i个数也能够加进来了..
    不知道说清楚没有, 总之我是看得懂自己写的这些东西,(貌似是废话..)
    但是还有一种情况要考虑阿, 因为这是动态规划, 而且使用两个循环来实习的, 如
    for(i = 2; i <= n; i++)
    for(j = 1; j <= tot; j++)
    …..
    在这种情况下, 如果直接把上面的….替换成f[I][j] = f[i – 1][j] + f[i – 1][j – i]就错了(Ps: 我提交的时候就是直接这样子的, 不知道为什么两个网站AC了~), 因为j – i可能会小于0 自然而然是不合法的, 所以需要判断一下, 如果j – i < 0的话f[i][j] = f[i – 1][j]..
    C语言:

    /*
    LANG: C
    ID: zqy11001
    PROG: subset
    */
    #include <stdio.h>
    
    #define min(a, b) ((a) < (b) ? (a) : (b))
    
    int f[40][390];
    
    int main(void)
    {
     int n, tot;
     int i, j,t;
     freopen(subset.in, r, stdin);
     freopen(subset.out, w, stdout);
     scanf(%d, &n);
     tot = n*(n + 1) >> 1;
     if(tot & 1){
     printf(\\n);
     return 0;
     }
     tot = tot >> 1;
     f[1][1] = 1;
     for(i = 2; i <= n; i++){
     for(j = 1; j <= tot; j++){
     f[i][j] = f[i - 1][j];
     if(j - i >= 0){
     f[i][j] += f[i - 1][j - i];
     }
     }
     }
     printf(%d\\n, f[n][tot]);
     return 0;
    }
  • Packing Rectangles 铺放矩形块 (IOI 95)

    Packing Rectangles 铺放矩形块 (IOI 95)

    在网上看了挺多解释这一题的文章,总感觉没看大懂,所以自己写一篇文章吧。希望能够帮助大家理解一下这一题。题目如下:

    http://www.nocow.cn/index.php/Translate:USACO/packrec

    开始拿到题目确实没怎么看懂,网上也有很多人说它是个水题,我倒感觉不以为然,我看了一些网上的人写的代码,说它是水题的人都是怎么写的。整个程序框架就是第一种情况循环+判断,第二种情况循环+判断,…………(以此类推)

    接下来我来说下我的做法,(我的语言表达能力却是不强,可能会看不懂,所以如果你看不懂分析的话直接看代码的注释吧。)

    对于任何一个矩形都有两种数据:长和宽,然而在这个程序中这方面是最难处理的也就是如何交换长和宽,也就是说在同一个矩形会拥有两种状况——横着摆,竖着摆。我就用srch1这个函数来实现所有矩形长和宽的交换。

    接下来就是位置的问题,该把哪个矩形放在哪里面积最小呢呢?当然,这个是看不出来的。所以也需要进行反复的迭代,我用srch2来实现的。

    最后再就是判断6种情况(其实只有五种,4和5是一样的)。

    (不知道描述清楚没有。)接下来我把代码进行一下简单的注释吧。

    C语言: Codee#12439

    /*
    LANG: C
    ID: zqy11001
    PROG: packrec
    QQ: 328400264
    */
    #include <stdio.h>
    
    #define swap(a, b) do{\\
     a ^= b;\\
     b ^= a;\\
     a ^= b;\\
    }while(0);
    #define max(a, b) ((a)>(b)?(a):(b))
    
    int m;
    int ans[300], s1[4], s2[4];
    int a[4], b[4];
    
    void update(int x, int y)
    {
     if(x * y < m){
     m = x * y;
     memset(ans, 0, sizeof(ans));
     }
     if(x * y == m){
     ans[x] = ans[y] = 1;
     }
    }
    
    int box(int x1, int y1, int x2, int y2, int x3, int y3,
     int x4, int y4)
    {
     int x, y;
     x = x1 + x2 + x3 + x4;
     y = max(max(y1, y2), max(y3, y4));
     update(x, y);
    
     x = max(x1, x2 + x3 + x4);
     y = y1 + max(y2, max(y3, y4));
     update(x, y);
    
     x = x1 + max(x2, x3 + x4);
     y = max(y1, y2 + max(y3, y4));
     update(x, y);
    
     x = x1 + x4 + max(x2, x3);
     y = max(y1, max(y4, y2 + y3));
     update(x, y);
    
     x = max(max(x1 + x2, x3 + x4), x1 + x4);
     y = max(max(y1 + y3, y2 + y4), y2 + y3);
     update(x, y);
    }
    
    void srch2(int n)
    {
     int i;
     if(n == 4){
     box(a[s2[0]], b[s2[0]], a[s2[1]], b[s2[1]],
     a[s2[2]], b[s2[2]], a[s2[3]], b[s23]]);
     }else{
     for(i = 0; i < 4; i++){
     if(!s1[i]){
     s2[n] = i;
     s1[i] = 1;
     srch2(n + 1);
     s1[i] = 0;
     }
     }
     }
    }
    
    void srch1(int n)
    {
     if(n == 4){
     memset(s1, 0, sizeof(s1));
     srch2(0);
     }else{
     srch1(n + 1);
     swap(a[n], b[n]);
     srch1(n + 1);
     swap(a[n], b[n]);
     }
    }
    
    int main(void)
    {
     int i, j;
     freopen(packrec.in, r, stdin);
     freopen(packrec.out, w, stdout);
     for(i = 0; i < 4; i++){
     scanf(%d%d, &a[i], &b[i]);
     }
     m = 10000;
     srch1(0);
     j = sqrt(m);
     printf(%d\\n, m);
     for(i = 1; i <= j; i++){
     if(ans[i]){
     printf(%d %d\\n, i, m / i);
     }
     }
     return 0;
    }

    接下来我对函数一个个进行一下简单的分析吧。
    首先

    #define swap(a, b) do{\\
     a ^= b;\\
     b ^= a;\\
     a ^= b;\\
    }while(0);
    #define max(a, b) ((a)>(b)?(a):(b))

    这两个宏是很简单的,swap把a和b交换一下位置,max是求两者最大值。

    Tip: 对于swap的话,三个异或就能完成问题,这个可能部分读者看不懂,本文是对这个题目的分析,如果看不大懂的话就改成

    #define swap(a, b) do{\\
     int t;\\
     t = a;
     a = b;
     b = t;
    }while(0);

    然后再是对变量进行分析,ans是存储结果用的,因为题目要求输出的时候要从小到大,所以使用ans来存储,输出的时候判断如果当前的值是1的话就输出,不然的话就继续寻找下一个。m是面积最大值。a和b是存储长和宽的。s1和s2到要用的时候再介绍吧。

    box函数就是对六种情况算面积,再用update函数来更新m和ans两个全局变量,如果这一种情况求出的面积和m的一样,那么就用这个面积的长和宽来更新ans。如果面积比m还要小,那么将m设置成这个面积,将ans置空,再用长和宽来更新ans。

    接下来先介绍srch1吧。

    void srch1(int n)
    {
     if(n == 4){
     memset(s1, 0, sizeof(s1));
     srch2(0);
     }else{
     srch1(n + 1);
     swap(a[n], b[n]);
     srch1(n + 1);
     swap(a[n], b[n]);
     }
    }

    这个n表示当前这种方案的下标。

    这个函数可以这么理解,

    先直接调用一次srch1(n + 1);就是枚举四个矩形用a做底,b做高的所有情况,接下来在把第n个矩形的长和高调一下位置再srch1(n + 1).因为刚刚把第n个矩形的长和高换了位置,所以还要把它换回来。

    然后当n==4的时候,四个矩形都递归完了,就该做些什么了,做些什么呢?

    继续看就知道了。

    调用srch2之前为什么要把s1置0呢`?

    看看srch2是怎么写的`

    void srch2(int n)
    {
     int i;
     if(n == 4){
     box(a[s2[0]], b[s2[0]], a[s2[1]], b[s2[1]],
     a[s2[2]], b[s2[2]], a[s2[3]], b[s2[3]]);
     }else{
     for(i = 0; i < 4; i++){
     if(!s1[i]){
     s2[n] = i;
     s1[i] = 1;
     srch2(n + 1);
     s1[i] = 0;
     }
     }
     }
    }

    可能有读者要说了,这跟srch1有点像啊.. 好吧, 我承认, 我摘抄了srch1的部分代码,但是srch2的功能未必也是将第n个矩形的长和高交换?肯定不是啦。

    当n==4的时候就是真正的进行判断了,所以理解else是重点.本函数的功能是,对四个矩形的长和高调用box的时候的顺序进行反复的交换。所以else里面是一个for循环,这里s1和s2就是用来判断重复的情况的,s1标志当前这个矩形是否已经被使用了?s2标志当前这个矩形的实质是什么?

    汗,,自己都没看懂这段话,再理一下。。

    s2[i]的意思是第i个矩形实际上是第几个矩形。因为四个矩形要反复的进行交换,所以s2就是交换的中间变量。那有人会问了。

     if(!s1[i]){
     s2[n] = i;
     s1[i] = 1;
     srch2(n + 1);
     s1[i] = 0;
     }

    这段是什么意思呢? 呵呵,你们想想,如果直接

    s2[n] = i;
    
    srch2(n + 1);

    的话那会有这种可能s2[0] == 0, s2[1] == 0, s2[2]==0, 也就是说在调用box的时候这些矩形分身了。s1在这里就是用来防止这种情况,s1用来标志这一个矩形是否被别的s2给使用了,如果使用了那s1就会是1,那么循环只好继续。

    整个程序大概分析完了。。

    如果还没看懂,那不是你蠢了,是我没写清楚吧。

    总之如果还有问题,联系我的QQ:328400264

  • ISBN号码 解题报告

    这题很简单,一位一位算就是,唯一要注意的就是mod 11 = 10的时候,要用X。代码:

    C语言:

    #include <stdio.h>
    #include <ctype.h>
    char isbn[14];
    
    char getint(void)
    {
     static int i = 0;
     int t;
     do{
     t = isbn[i++];
     }while(!isdigit(t));
     return t - \'0\';
    }
    
    int main(void)
    {
     int a = 0;
     int i;
     char t;
     scanf(%s, isbn);
     for(i = 1; i <= 9; i++){
     t = getint();
     a += t * i;
     }
     t = isbn[12];
     a %= 11;
     if(a == 10){
     a = \'X\';
     }else{
     a += \'0\';
     }
     if(t == a){
     printf(Right\\n);
     }else{
     isbn[12] = a;
     printf(%s\\n, isbn);
     }
     return 0;
    }
  • 不高兴的津津 解题报告

    不知道这题该属于哪个类别,,贪心?枚举?反正不难,,代码:

    C语言:

    #include <stdio.h>
    
    int main(void)
    {
     int i;
     int a, b;
     int ans = 0, t = 0;
     for(i = 1; i <= 7; i++){
     scanf(%d%d, &a, &b);
     if((a + b > 8) && (a + b > t)){
     ans = i;
     t = a + b;
     }
     }
     printf(%d\\n, ans);
     return 0;
    }
  • 计数的梦 解题报告

    这题的话,怎么说呢?就是暴力!!

    C语言:

    #include <stdio.h>
    int ans[10];
    
    void count(int num)
    {
     while(num != 0){
     ans[num % 10]++;
     num /= 10;
     }
    }
    
    int main(void)
    {
     int i, j;
     scanf(%d%d, &i, &j);
     while(i <= j){
     count(i++);
     }
     for(i = 0; i < 10; i++){
     if(i != 0){
     printf( );
     }
     printf(%d, ans[i]);
     }
     printf(\\n);
     return 0;
    }
  • A+B Problem 解题报告

    这题不会做,那你就应该砸机子了。

    C语言:

    #include <stdio.h>
    
    int main(void)
    {
     int i, j;
     scanf(%d%d, &i, &j);
     printf(%d\\n, i + j);
     return 0;
    }
  • Web浏览 解题报告

    开到这个题目,想起了浏览器Lynx,用过两次,太难用了,汗,,貌似跑题了。
    整个题目就是要了解一下浏览器的向前向后的特性,浏览器向后几步之后再输入网址就不能够向前浏览了。整个程序我就用一个数组来维护的,额,具体的细节看代码吧:

    C语言:

    #include <stdio.h>
    #include <string.h>
    #define MAX 10000
    char link[MAX][71];
    int tail, now;
    
    void add(char *str)
    {
     int t = (now + 1) % MAX;
     if(now == MAX){
     exit(-1);
     }
     strcpy(link[t], str);
     now = t;
     tail = now + 1;
    }
    
    char *back(void)
    {
     if(now == 0){
     return NULL;
     }
     return link[--now];
    }
    
    char *forward(void)
    {
     if(now + 1 == tail){
     return NULL;
     }
     return link[++now];
    }
    
    void init(void)
    {
     tail = 0;
     now = -1;
     add(http://www.acm.org/);
    }
    
    int main(void)
    {
     char *t;
     char command[8], address[71];
    
     init();
     while(scanf(%s, command)){
     if(strcmp(command, BACK) == 0){
     t = back();
     if(t == NULL){
     printf(Ignored\\n);
     }else{
     printf(%s\\n, t);
     }
     }else if(strcmp(command, FORWARD) == 0){
     t = forward();
     if(t == NULL){
     printf(Ignored\\n);
     }else{
     printf(%s\\n, t);
     }
     }else if(strcmp(command, VISIT) == 0){
     scanf(%s, address);
     add(address);
     printf(%s\\n, address);
     }else{
     break;
     }
     }
    
     return 0;
    }
  • 谁拿了最多奖学金 解题报告

    这题我就什么都不说了吧,这题不会做你就回去把语言好好学学,学习输入输出的一些空格和换行。

    C语言:

    #include <stdio.h>
    #define getint(i) scanf(%d, &i)
    
    int main(void)
    {
     int n, i;
     int t1, t2, t3;
     char c2, c1;
     int tot = 0;
     int max = 0;
     int grade;
     char maname[100], name[100];
     getint(n);
     for(i = 0; i < n; i++){
     grade = 0;
     scanf(%s%d%d %c %c%d\\n, name, &t1, &t2, &c1, &c2, &t3);
     if((t1 > 80) && (t3 > 0)){
     grade += 8000;
     }
     if((t1 > 85) && (t2 > 80)){
     grade += 4000;
     }
     if(t1 > 90){
     grade += 2000;
     }
     if((t1 > 85) && (c2 == \'Y\')){
     grade += 1000;
     }
     if((t2 > 80) && (c1 == \'Y\')){
     grade += 850;
     }
     if(grade > max){
     strcpy(maname, name);
     max = grade;
     }
     tot += grade;
     }
     printf(%s\\n%d\\n%d\\n, maname, max, tot);
     return 0;
    }
  • 校门外的树 解题报告

    这题没什么好说的,我觉得就是用数组判断一下,我用memset优化了一些慢效率的循环,本来以为会超时,但是结果完全相反,速度还挺快。。
    代码如下:

    C语言:

    #include <stdio.h>
    #include <string.h>
    char map[10001];
    
    int main(void)
    {
     int i, j;
     int a, b;
     int l, n;
     int ans = 0;
    
     scanf(%d%d, &l, &n);
     memset(map, 1, l + 1);
     for(i = 0; i < n; i++){
     scanf(%d%d, &a, &b);
     memset(map + a, 0, b - a + 1);
     }
     for(i = 0; i <= l; i++){
     if(map[i]){
     ans++;
     }
     }
     printf(%d\\n, ans);
    
     return 0;
    }
  • Noip 2006 作业调度方案

    题目的原描述如下,rqnoj和vijos的题目都不完全,少了一幅图片,表格也不清晰。。

    【问题描述】

    我们现在要利用m台机器加工n个工件,每个工件都有m道工序,每道工序都在不同的指定的机器上完成。每个工件的每道工序都有指定的加工时间。

    每个工件的每个工序称为一个操作,我们用记号j-k表示一个操作,其中j为1到n中的某个数字,为工件号;k为1到m中的某个数字,为工序号,例如2-4表示第2个工件第4道工序的这个操作。在本题中,我们还给定对于各操作的一个安排顺序。

    例如,当n=3,m=2时,“1-1,1-2,2-1,3-1,3-2,2-2”就是一个给定的安排顺序,即先安排第1个工件的第1个工序,再安排第1个工件的第2个工序,然后再安排第2个工件的第1个工序,等等。

    一方面,每个操作的安排都要满足以下的两个约束条件。

    (1) 对同一个工件,每道工序必须在它前面的工序完成后才能开始;

    (2) 同一时刻每一台机器至多只能加工一个工件。

    另一方面,在安排后面的操作时,不能改动前面已安排的操作的工作状态。

    由于同一工件都是按工序的顺序安排的,因此,只按原顺序给出工件号,仍可得到同样的安排顺序,于是,在输入数据中,我们将这个安排顺序简写为“1 1 2 3 3 2”。

    还要注意,“安排顺序”只要求按照给定的顺序安排每个操作。不一定是各机器上的实际操作顺序。在具体实施时,有可能排在后面的某个操作比前面的某个操作先完成。

    例如,取n=3,m=2,已知数据如下:

    工件号 机器号/加工时间
    工序1 工序2
    1 1/3 2/2
    2 1/2 2/5
    3 2/2 1/4

    则对于安排顺序“1 1 2 3 3 2”,下图中的两个实施方案都是正确的。但所需要的总时间分别是10与12。

    (原来这里有图,但搬运过来丢失了)

    当一个操作插入到某台机器的某个空档时(机器上最后的尚未安排操作的部分也可以看作一个空档),可以靠前插入,也可以靠后或居中插入。为了使问题简单一些,我们约定:在保证约束条件(1)(2)的条件下,尽量靠前插入。并且,我们还约定,如果有多个空档可以插入,就在保证约束条件(1)(2)的条件下,插入到最前面的一个空档。于是,在这些约定下,上例中的方案一是正确的,而方案二是不正确的。

    显然,在这些约定下,对于给定的安排顺序,符合该安排顺序的实施方案是唯一的,请你计算出该方案完成全部任务所需的总时间。

    【输入文件】

    输入文件jsp.in 的第1行为两个正整数,用一个空格隔开:m n(其中m(<20)表示机器数,n(<20)表示工件数)

    第2行: 个用空格隔开的数,为给定的安排顺序。

    接下来的2n行,每行都是用空格隔开的m个正整数,每个数不超过20。

    其中前n行依次表示每个工件的每个工序所使用的机器号,第1个数为第1个工序的机器号,第2个数为第2个工序机器号,等等。

    后n行依次表示每个工件的每个工序的加工时间。

    可以保证,以上各数据都是正确的,不必检验。

    【输出文件】

    输出文件jsp.out只有一个正整数,为最少的加工时间。

    【输入样例】

    2 3

    1 1 2 3 3 2

    1 2

    1 2

    2 1

    3 2

    2 5

    2 4

    【输出样例】

    10

    =================================华丽的分割线===================================

    引用一位仁兄的话吧,现在在进行“素质教育”,光OI好已经是不行了,还要语文全面发展。。(哈哈,稍加修改了)
    整个程序的思路就是模拟,其实真的很容易,不过这种模拟题我还真没做过,所以Wa了N次,Wa的原因都写在注释里了,还有就是求ans的过程我也集成在主循环里了,降低了可读性,提高了点效率,Sorry各位观众了(算是读者啦)。
    代码如下:

    C语言:

    #include <stdio.h>
    #include <string.h>
    int train[361];
    int machine[19][19];
    int time[19][19];
    int used[19];
    int finished[19];
    char cpu[19][361];
    
    int main(void)
    {
     int i, j, k;
     int m, n;
     int t;
     int ans;
     scanf(%d%d, &m, &n);
     for(i = 0; i < m * n; i++){
     scanf(%d, &train[i]);
     train[i]--;
     }
     for(i = 0; i < n; i++){
     for(j = 0; j < m; j++){
     scanf(%d, &machine[i][j]);
     machine[i][j]--;
     }
     }
     for(i = 0; i < n; i++){
     for(j = 0; j < m; j++){
     scanf(%d, &time[i][j]);
     }
     }
    
     ans = 0;
     for(i = 0; i < m * n; i++){
     t = train[i];
     j = finished[t] - 1; // 因为当查找失败时,j的值需要向前增一, 
     do{ //所以在赋值的时候就减了一,然后用do-while 
     j++; //的形式,一进来就对j递增。 
     for(k = 0; k < time[t][used[t]]; k++){
     if(cpu[machine[t][used[t]]][j + k]){
     j = k + j; // 在最开始我就错在这行代码j = k + j 
     break; //的位置上,我把位置放在while(..)的上面, 
     } //也就导致了在有时候选择了处理器新的位置
     } //时在底下的memset功能工作不正常。因为我
     //是使用的finished而不是j,则在选择了位置
     //之后会导致一些预想不到的问题。
     }while(cpu[machine[t][used[t]]][j]);
     memset(cpu[machine[t][used[t]]] + j, t + 1, time[t][used[t]]);
     if(ans < j + time[t][used[t]]){
     ans = j + time[t][used[t]];
     }
     finished[t] = j + k;
     used[t]++;
     }
     // 最后还来一点注释,我把求ans集成
     //到了主循环里面,牺牲了一些可读性,对 
     //各位朋友说声Sorry..
     printf(%d\\n, ans);
     return 0;
    }