URAL 做题记录



1005. Stone Pile

///背包思想,就是满背包思想,尽可能的装满一半或者一半多1的量!   其实数据量不大,还可以用爆搜,对于每一种物品,选择房还是不放!  DFS,然后对全局变量ans取最小的结果既可以了
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <math.h>
using namespace std;
const int MAX = 2004000;
int dp[MAX];
int a[25];
int sum;
int main()
{
    //freopen("in","r",);
    int n,k;

    while( ~scanf("%d",&n) )
    {
        sum=0;
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;++i)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }

        int cnt=(sum+1)/2;///这里为什么要加1呢? 因为尽可能的装满一半,或者一半多一点;
        dp[0]=1;
        int ans=0;
        for(int i=0;i<n;++i)
        {
            for(int j=cnt;j>=a[i];j--)
            {
                if(dp[j-a[i]])
                {
                    if(j>ans)ans=j;
                    dp[j]=1;
                }
            }
        }
        ans=sum-ans-ans;
        ans=ans<0?-ans:ans;
        printf("%d\n",ans);
    }
return 0;
}

///下面这个代码是别人的DFS
#include<iostream>
#include<stdlib.h>
#define maxn 27
#define INF 20000007
using namespace std;
long n,sum,ans,w[maxn];
void dfs(long dep,long now)
{
    if(dep>n)
    {
        if(ans>abs(now-(sum-now))) ans=abs(now-(sum-now));
        return;
    }
    dfs(dep+1,now);
    dfs(dep+1,now+w[dep]);
}
int main()
{
    cin>>n;
    sum=0;
    for(long i=1;i<=n;i++)
    {
        cin>>w[i];
        sum+=w[i];
    }
    //  Input
    ans=INF;
    dfs(1,0);
    cout<<ans<<endl;
return 0;
}
来源: <http://www.cppblog.com/rakerichard/archive/2010/09/05/125959.html>




1009. K-based Numbers


dp1009
#include<iostream>
#include<limits>
#include<cstring>
#include<cmath>
using namespace std;

long long dp[20][20];
///dp[i][0]表示某位为0的可行的组合,dp[i][1]表示某位上为1的可行的组合
/// 最开头的位不能为0,然后某一位可以为0的话要看前面的数字是否是非0的;
///如果要为1的话要看前面的数字为0和非0的个数*这一位可能的情况k-1
///本来这道题想要用容斥原理去做,先求n-1上非法的组合数,然后再*(k-1)即可,但是前面较小的
///数据都能过但是较大的数字过不了,对拍之后!  所以可能的情况是:这道题不适合用容斥原理,因为
///他本身就不符合容斥原理!  
int main()
{
    long long n,k;
    long long ans;

    while(cin>>n>>k)
    {
        dp[0][0]=0;
        dp[0][1]=k-1;
        for(int i=1;i<n;++i)
        {
            dp[i][0]=dp[i-1][1];
            dp[i][1]=(dp[i-1][0]+dp[i-1][1])*(k-1);
        }
        cout<<dp[n-1][1]+dp[n-1][0]<<endl;
    }

    return 0;
}

1010. Discrete Function

该死的题目,错了N次,刚开始以为是计算几何,没敢做,后来大着胆子做了做发现没那么难,就用刚学的计算几何叉积去做,后来怎么做怎么错,一看数据类型不对,又改! 改了半天发现还得用大数去做,真麻烦,……  但是后来在人家的解题报告当中才明白是自己读错题意了,找坡度最大的两点组成的连线,并且两点间的点不在连线的上面,(其实如果在下面的话又可以组成新的符合要求的线!)  哎郁闷
There is a discrete function.//离散的
function for which all points  You have to find such two points of the between them are below than straight line connecting them andinclinationof this straight line is the largest.
#include<iostream>
#include<cmath>
#include<stdlib.h>
using namespace std;
long long num[100005];

int main()
{
    int n;cin>>n;
    int ans1=1,ans2=2;
    
    cin>>num[1]>>num[2];
    long long tmax=llabs(num[2]-num[1]);

    for(int i=3;i<=n;++i)
    {
        cin>>num[i];
        if(llabs(num[i]-num[i-1])>tmax){tmax=llabs(num[i]-num[i-1]); ans1=i-1;ans2=i;}
    }
    cout<<ans1<<" "<<ans2<<endl;

    return 0;
}




1011. Conductors

可算是被这个题坑惨了,改了好多遍都没A !
给你两个数,P,Q就是说这个城市的人大约(P%~Q%)的人做某种职业,问总人口至少有多少人?
那么枚举总人口就好了,保证做某种职业的正好是存在……   有两点一是保存精度,二是注意开区间?  讨厌这种精度题!

附上别人的代码,还需要继续研究……

#include <iostream>
#include <cmath>
using namespace std;

int main()
{
	double p, q, i;
	cin>>p>>q;
	p = p * 0.01 + 1e-8; //严格按照题目要求
	q = q * 0.01 - 1e-8;
	for (i=1; floor(i*q) - ceil(i*p)<0; i++);
	cout<<i<<endl;
}

1012. K-based Numbers. Version 2

给你一个n个位的k进制数,然后判断有多少个数符合没有两个0连续的情况!  注意0不能做最高位!
这是个DP题,但是得用大数去做!   看来题目用什么方法去解决还得看数据量啊!

大数的算法我刚刚看懂,代码是小媛的: http://blog.csdn.net/zxy_snow/article/details/6381579
女神的代码真心不好懂啊!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <math.h>
using namespace std;
const int MAX = 1010;
int dp[20][200][MAX];
int len[20][200];
int main()
{
    int n,k;
    memset(dp,0,sizeof(dp));
    for(int i=2; i<=10; i++)
    {
        len[i][1] = len[i][2] = 1;
        dp[i][1][0] = i-1;
        dp[i][2][0] = (i-1)*i;
    }
    for(int i=2; i<=10; i++)///控制进位制
        for(int k=3; k<=180; k++)///控制位数
        {
            int mlen = max(len[i][k-1],len[i][k-2]);///获取位数
            for(int j=0; j<mlen; j++)
            {
                dp[i][k][j] += (i-1)*(dp[i][k-1][j] + dp[i][k-2][j]);///   i*(i-1)*其他=(i-1)*i*其他

                if( dp[i][k][j] >= 10 )
                {///进位
                    dp[i][k][j+1] += dp[i][k][j]/10;
                    dp[i][k][j] %= 10;
                }
            }
            int j = mlen;
            if( dp[i][k][j] )
                j++;

            while( dp[i][k][j] >= 10 )
            {///再进位
                dp[i][k][j+1] += dp[i][k][j]/10;
                dp[i][k][j] %= 10;
                j++;
            }
            len[i][k] = j;
        }



    while( ~scanf("%d%d",&n,&k) )
    {
        for(int i=len[k][n]-1; i>=0; i--)///为什么要循环输出呢?!  因为是大数嘛!  所以一个存不下啊!!
            printf("%d",dp[k][n][i]);
        printf("\n");
    }
    return 0;
}






1014. Product of Digits

 给一个n,找出最小的数,使其每一位的乘积=n;(0<=n<10^9)

想了预处理或者分段预处理,但都没能解决这个题,本来我是按照,先用最小的除,然后用较大的输出,把能整出的凑成一个数!  但是发现不对啊

比如16=(2222)是错误的! 

后来看了别人的代码才明白,是贪心处理,否则没有更好的高效的算法……  当初就应该想到的,仔细分析下题意,明白题目本质就好了!

让ans最小,那么除最高位之外的每一位尽可能的大,才能使的总位数最少,而且能保证位数相同的情况下最高位最小,这样才能保证整体最小(最后逆序输出)

总结一下:  要想最小,首先位数少; 在总位数相同的情况下,最高位最小!

#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;

int main()
{///1014
    int n, i, j = 0;
    cin>>n;
    if (0 == n) { cout<<10<<endl;return 0 ;}
    else if (1 == n){ cout<<1<<endl; return 0;}///!!!这里的答案很无语啊
    int a[100];
    for (i = 9; i > 1; i--)
        while (n % i == 0)
        {///循环多次/i,直到不能整除
            a[j] = i;   //cout<<i<<" ==== "<<a[j]<<endl;
            j++;
            n /= i;
        }
    if (a[0] == 0 || n != 1)
        cout<<-1<<endl;
    else  for (j--; j >= 0; j--) cout<<a[j];///逆序输出

    return 0;

}



给一棵苹果二叉树,每个节点随机编号,分别是1--n,不重复,然后保留其中的q个枝条,每个纸条上都有一定数量的苹果,那么求剩下的苹果的数量最多是多少!

就好比图,边的权值就是苹果的数目!

此题用的是动态规划,将边上的权值下放到节点上,然后又知道此题1节点必须是根节点(而且跟节点不能去掉),那么好了; 此题就是最后dp[1][q];   保留根节点1还有q个枝条能剩下多少苹果!   那么这q个枝条到底是从左子树上来的还是从右子树上来的?!  那怎么办呢?!  枚举对于一个节点pot,枚举i ,TreeDP(tree[pot].lc,i)+TreeDP(tree[pot].rc,q-i)   然后从中选一个最小的!



#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define MAX 110
typedef struct
{
    int val;
    int lc,rc;///左右孩子
}Node;
Node tree[MAX];
int vis[MAX];
int n,q;
int map[MAX][MAX];
int dp[MAX][MAX];
void CreateTree(int pot)
{
    vis[pot]=1;
    int li=0,ri=0;
    for(int i=1;i<=n;++i)
    {
        if(!vis[i]&&map[pot][i])
        {
            if(!tree[pot].lc){li=i;tree[pot].lc=i;  tree[i].val=map[pot][i];}///这里不光指示边的权值下方,还指明了左右儿子的编号
            else{ri=i; tree[pot].rc=i;   tree[i].val=map[pot][i]; }
         ///  CreateTree(i);///这样子找到一个孩子就直接建树或者是找到两个孩子再建树都可以的!
        }
    } if(li)CreateTree(li);///递归建树
     if(ri)CreateTree(ri);///递归建树
}

int TreeDP(int pot,int e)
{///dp,树形DP
    if(!pot||!e)return 0;
    if(tree[pot].lc==0)return tree[pot].val;///叶子几节点
    if(dp[pot][e])return dp[pot][e];///这一行没有的话会死循环
    int mmax=0;
    for(int i=0;i<e;++i)
    {
        int lnum=TreeDP(tree[pot].lc,i);
        int rnum=TreeDP(tree[pot].rc,e-i-1);
        mmax=max(mmax,lnum+rnum);
    }
    dp[pot][e]=mmax+tree[pot].val;///必须要加上tree[pot].val 否则地步无法向上传递
 //   cout<<"ok:"<<dp[pot][e]<<endl;
    return dp[pot][e];
}

void init()
{
    memset(dp,0,sizeof(dp));
    memset(map,0,sizeof(map));
    memset(vis,0,sizeof(vis));
    memset(tree,0,sizeof(tree));
}
int main()
{
    init();
    scanf("%d%d",&n,&q);
    int u,v,num;
    for(int i=1;i<n;++i)
    {
        scanf("%d%d%d",&u,&v,&num);
        map[u][v]=map[v][u]=num;
    }
    CreateTree(1);
    cout<<TreeDP(1,q+1)<<endl;///因为把权值都下放到点上了,所以点数应该比边数大一,因为还有子节点
    /// cout<<dp[1][q]<<endl;///注意这里输出的应该是dp[1][q+1]  因为点数比边大1
    return 0;
}




1020. Rope

///给n个点,按照 The nails are described either in a clockwise or in a counterclockwise order starting from an arbitrary nai
///但是每个钉子是一个圆形,也得计算进去,其实直接计算所构成的多变形的边长再加上一个圆的周长就好了,因为多边形的各个角正好对应360°
#include <queue>
#include <stack>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <limits.h>
#include <string.h>
#include <algorithm>
#include<cmath>
using namespace std;

int n;
double r;
///double pai=3.141592;
const double pai = acos(-1.0);
double dis(double x,double y,double x1,double y1)
{

    return sqrt( (x-x1)*(x-x1)+(y-y1)*(y-y1) );
}
int main()
{
    while(~(scanf("%d%lf",&n,&r)))
    {
        double ans=0.0;
        double x[105],y[105];//,x1,y1,tx,ty;
        scanf("%lf%lf",&x[0],&y[0]);
        for(int i=1;i<n;++i)
        {///我的是读数据和处理一块处理,方便
            scanf("%lf%lf",&x[i],&y[i]);
            if(i==n-1)ans+=dis(x[i],y[i],x[0],y[0]);
            ans+=dis(x[i],y[i],x[i-1],y[i-1]);
        }if(n!=1)ans+=dis(x[n-1],y[n-1],x[0],y[0]);///考虑了N==1的情况!其实多次一举,因为本身到本身的距离为0,
                                                    ///但是这样处理的思想很好!
        ans+=pai*r*2;
        printf("%.2lf\n",ans);

    }

     return 0;
}

const double pi = acos(-1.0);
const int MAX = 110;
struct point{ double x,y;};
point p[MAX];
double disp2p(point a,point b)
{
    return sqrt( ( a.x - b.x ) * ( a.x - b.x ) + ( a.y - b.y ) * ( a.y - b.y ) );
}
int main()
{
    int n;
    double r;
    while( ~scanf("%d%lf",&n,&r) )
    {
        for(int i=0; i<n; i++)
            scanf("%lf%lf",&p[i].x,&p[i].y);
        double sum = 0.0;
        for(int i=0; i<n; i++)
            sum += disp2p(p[i],p[(i+1)%n]);///先读完数据在处理很OK但是有点小浪费时间

        printf("%.2lf\n",sum+2*pi*r);
    }
return 0;
}

1021. Sacrament of the Sum

给两个序列,问从分别从每个序列里面取一个数看其和是否能等于10000!
每个list最大有5W,所以n^2可能面临超时的危险! 那么怎么做呢!  题目给的是已经排好序的list,那么可以利用已经排好序的……      否则也可以利用这样一个原理:  num1<10000  如果sum3[10000-num1]=true;  sum3记录的是另一个序列的正数i是否存在!  利用数组下标直接定位!
自己的代码分成四个sum,正负还有list1,list2之分!  其实完全可以全部增加一个数,全部转化成正数就好做了……
#include<algorithm>
#include <iostream>
#include <cmath>
#include<cstring>
using namespace std;

const int key=10000;
int n1,n2;
int num1[50005],num2[50005];
int sum1[50000],sum2[50000];
int sum3[50000],sum4[50000];
int main()
{
    memset(num1,0,sizeof(num1));
    memset(num2,0,sizeof(num2));
    memset(sum1,0,sizeof(sum1));
    memset(sum2,0,sizeof(sum2));

 memset(sum3,0,sizeof(sum1));
    memset(sum4,0,sizeof(sum2));

    cin>>n1;
    for(int i=0;i<n1;++i)
    {
        cin>>num1[i];
        if(num1[i]>=0)
        {
            sum1[num1[i]]=1;
        }else sum2[-num1[i]]=1;
    }
    cin>>n2;
    for(int i=0;i<n2;++i)
    {
        cin>>num2[i];
        if(num2[i]>=0)
        {
            sum3[num2[i]]=1;
        }else sum4[-num2[i]]=1;
    }
    bool bo=false;
    for(int i=0;i<n1;++i)
    {
        if(num1[i]>=key)
        {
            if(sum4[num1[i]-key]){bo=true;break;}
        }else if(num1[i]<key)
        {
            if(sum3[key-num1[i]]){bo=true;break;}
        }else
        {
            if(sum3[num2[i]+key]){bo=true;break;}
        }
     }
//    cout<<sum1[10424]<<endl;
//    cout<<sum4[424]<<endl;
    if(bo)cout<<"YES"<<endl;
    else cout<<"NO"<<endl;


    return 0;
}



或者利用已经排好的顺序:
#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN=50005;

int main()
{
//  freopen("data.in","r",stdin);
//  freopen("data.out","w",stdout);
  int c,cc,num[MAXN];
  cin >> c;
  for (int i=0;i<c;++i) cin >> num[i];
  num[c]=31440461;
  cin >> cc;
  int p=0;
  for (int i=0;i<cc;++i)
    {
      int x;
      cin >> x;
      while (x+num[p]<10000) ++p;
      if (x+num[p]==10000) { cout << "YES\n"; return 0; }
    }
  cout << "NO\n";
  return 0;
}

另一个版本的代码:
http://www.cnblogs.com/staginner/archive/2012/05/02/2478856.html
/*
 可以将问题等效成对于x查找10000-x是否在列表中出现过,二分、哈希等都可以。
*/
#include<stdio.h>
#include<string.h>
#define D 33000
#define MAXD 66000
#define MIN -32768
#define MAX 32767
int N, h[MAXD];
void init()
{
    int i, k;
    memset(h, 0, sizeof(h));
    for(i = 0; i < N; i ++)
    {
        scanf("%d", &k);
        h[D + k] = 1;
    }
}
void solve()
{
    int i, j, k, ok = 0;
    scanf("%d", &N);
    for(i = 0; i < N; i ++)
    {
        scanf("%d", &k);
        k = 10000 - k;
        if(k >= MIN && k <= MAX && h[D + k])
            ok = 1;
    }
    printf("%s\n", ok ? "YES" : "NO");
}
int main()
{
    while(scanf("%d", &N) == 1)
    {
        init();
        solve();
    }
    return 0;
}



1022. Genealogical Tree

题意到现在也不是很明白,网上搜了一下说是拓扑排序,于是看了下样例就开始敲!  
没想到实现了,1A;  拓扑排序自己以前做过不是自己实现的!   在敲这个题的时候还有点心虚!
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=10000004;
int map[120][120];
int cnt[120];
int n;
queue<int> que;
int ans[110];
int tot;
void f()
{
    for(int i=1;i<=n;++i)
    {
        if(cnt[i]==0)
        {
            que.push(i);
            cnt[i]=-1;
        }
    }
    while(!que.empty())
    {
         int x=que.front();que.pop();
         ans[tot++]=x;
         for(int i=1;i<=n;++i)
         {
            // cout<<i<<" "<<x<<"   "<<map[i][x]<<endl;
             if(map[i][x])
             {//cout<<"hahah"<<endl;
                 map[i][x]=0;
                 cnt[i]--;
             }

         }
        for(int i=1;i<=n;++i)
        {
            if(cnt[i]==0)
            {
                que.push(i);
                cnt[i]=-1;
            }
        }

    }

}
int main()
{
    memset(map,0,sizeof(map));
    memset(cnt,0,sizeof(cnt));
    while(cin>>n)
    {
        int x;
        for(int i=1;i<=n;++i)
        {
            while(cin>>x)
            {
               if(x==0)break;;
               map[i][x]=1;
               cnt[i]++;
            }
        }

       // que.clear();
        tot=0;
        f();  //cout<<"tot "<<tot<<endl;
        if(tot)
        {
            for(int i=tot-1;i>=0;--i)
                cout<<ans[i]<<" ";
            cout<<endl;
        }else cout<<"jiji"<<endl;
    }

    return 0;
}

别人的代码,链式前向星:
#include<stdio.h>
#include<string.h>
#define MAXD 110
#define MAXM 10010
int N, cnt, e, first[MAXD], next[MAXM], v[MAXM], topo[MAXD], vis[MAXD];
void add(int x, int y)
{
    v[e] = y;
    next[e] = first[x], first[x] = e ++;
}
void init()
{
    int i, j, k;
    memset(first, -1, sizeof(first));
    e = 0;
    for(i = 1; i <= N; i ++)
        for(;;)
        {
            scanf("%d", &k);
            if(k == 0)
                break;
            add(i, k);
        }
}
void dfs(int cur)
{
    int i;
    vis[cur] = 1;
    for(i = first[cur]; i != -1; i = next[i])
        if(!vis[v[i]])
            dfs(v[i]);
    topo[-- cnt] = cur;
}
void solve()
{
    int i, j, k;
    cnt = N;
    for(i = 1; i <= N; i ++)
        if(!vis[i])
            dfs(i);
    printf("%d", topo[0]);
    for(i = 1; i < N; i ++)
        printf(" %d", topo[i]);
    printf("\n");
}
int main()
{
    while(scanf("%d", &N) == 1)
    {
        init();
        solve();
    }
    return 0;
}

1023. Buttons

最简单的博弈,不解释……



1025. Democracy in Danger

选举问题,有多个小组,每组人数不固定,如果一个小组有一半以上的人数支持,那么这组对外表现为支持!
如果有半数以上的小组表示为支持,那么就表示为支持!   拍一下顺序就好了!
但是这个题数据很水……很多错误的代码都错!
错误代码,  测试: 4   2 2 2 2   错误输出2或者有的代码输出3
int n;
int num[110];
int main()
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>num[i];
    
    sort(num,num+n);
    int ans=0;
    for(int i=0;i<(n+1)/2;++i) 
        ans+=(num[i]+1)/2;
    
    cout<<ans<<endl;
    return 0;
}
 for(int i=0;i<(n+2)/2;++i)
        ans+=(num[i]+2)/2;  /// 这是正确的更改!    4  2 2 2 2  正确输出为6
for(i=0;i<=k/2;i++)
{///比较好理解的的解法
   sum+=arr[i]/2+1;
}




1028. Stars

给你一些星星的坐标,从坐标已经排好序,求每一个level的星星数,level怎么算呢?
一个星星如果有五颗星星横纵坐标都不超过他! 那么他的level就是5……
数据量是15000颗星星,模拟超时,想到树状数组,可以却莫名的超时了…… ,郁闷中,怎么办呢?!    本来以为还有更好的算法,或者
更高级的算法,于是搜解题报告,看到人家的代码也是数组,就是超时,各种郁闷,后来才知道,树状数组的起点必须是1,而体重所给的数据有0……  所以莫名其妙的超时了
看来对树状数组的理解还是不到位啊!  其实这个题用线段树都可以做,貌似用树状数组能解决的问题都能用线段树都能做!
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 32010


int n;
int ans[N];
int c[N];
int lowbit(int x)
{
    return x&(-x);
}

void update(int x)
{
    while(x<=N)
    {
        c[x]++;
        x+=lowbit(x);
    }
}
int getsum(int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=c[x];
        x-=lowbit(x);
    }
    return ans;
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        memset(c,0,sizeof(c));
        memset(ans,0,sizeof(ans));
        int x,y;
        for(int i=0; i<n; ++i)
        {
            scanf("%d%d",&x,&y);

            int t=getsum(x+1);///这里必须是x+1,因为数据中x会等于0,这样导致树状数组无法处理,导致
                                ///超时超时超时超时超时超时……
            ans[t]++;
            update(x+1);
        }

        for(int i=0; i<n; ++i)
            printf("%d\n",ans[i]);
    }



    return 0;
}





1033. Labyrinth

给一个矩阵,分为.和# ,然后四周有墙,#也是墙,问游客在迷宫里能找到多少面不同的墙,  左上角和右下角分别是入口,所以不算!
用BFS就好,好久没写了,还有点手生,但是毕竟是自己实现过的,理解了的算法,还是写出来了!   注意怎么处理边界,如果这个点是”.“  ,那就BFS,否则越界的话就是碰到一次墙了,ans++;  碰到#也算……   每个点有四次碰到墙的机会!
另外这个题用DFS也可以做,但是自己对DFS的理解没有BFS好,所以就先写成了BFS!    还有就是两个入口不一定是相通的,就是说中间被割断了,那就判断一下就好,大不了从两个端点开始BFS啊!   其他的非联通快则不用管!
这是今天做的最爽的一个题,是自己想出并且实现的!   

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

char map[35][35];
int n;

struct Node
{///便于封装放到队列里面!
    int x,y;
    char ch;
};
queue <Node> que;
int vis[35][35];
int dir[4][2]={0,1,  0,-1,   1,0 , -1,0 };
bool isok(int x,int y)
{
    if( x>=0&&x<n&&y>=0&&y<n )return true;
    return false;
}
int ans;
int  bfs(int x0,int y0)
{
    ans=0;
    memset(vis,0,sizeof(vis));
    vis[x0][y0]=1;
    Node t;
    t.x=x0;  t.y=y0;  t.ch=map[x0][y0];
    que.push(t);
    while(!que.empty())
    {
        t=que.front();que.pop();
        vis[t.x][t.y]=1;
        for(int i=0;i<4;++i)
        {
            Node tmp; tmp.x=t.x+dir[i][0];
            tmp.y=t.y+dir[i][1];
            if(!isok(tmp.x,tmp.y))
            {
                ans++;continue;
            }
            if(isok(tmp.x,tmp.y)&&!vis[tmp.x][tmp.y])
            {
                if(map[tmp.x][tmp.y]=='#')ans++;
                else
                {
                    tmp.ch=map[tmp.x][tmp.y];
                    que.push(tmp);
                    vis[tmp.x][tmp.y]=1;
                }
            }
        }
    }
    if(vis[n-1][n-1])return ans;///如果最后一个出口没有被访问,那么在调用一次BFS!
    else
    {
        int cnt=ans;
        cnt+=bfs(n-1,n-1);
        return cnt;
    }
}

int main()
{
    while(cin>>n)
    {
        for(int i=0;i<n;++i)
        {
            cin>>map[i];
        }
        int ans1=bfs(0,0);
       // int ans2=bfs(n-1,n-1);
        cout<<(ans1-4)*9<<endl;///为什么要-4?  因为两个路口那里算重了两次!
    }


    return 0;
}

 

下面附上Domacles的代码,比较漂亮的DFS

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>

using namespace std;

int x[4]= {1,-1,0,0};
int y[4]= {0,0,1,-1};
int n;
char Map[35][35]= {0};
int path[35][35]= {0};///数组至少是35,多于的一圈做哨兵!!  而且省了isok函数
int ans=0;
/*
这里的DFS没有回溯,但是也能够遍历!
没有漏掉,这是我一直不放心的地方!

*/
void DFS(int X,int Y)
{
    int i;
    path[X][Y]=1;
    for(i=0; i<4; i++)
    {
        if(Map[X+x[i]][Y+y[i]]=='.'&&path[X+x[i]][Y+y[i]]==0)
            DFS(X+x[i],Y+y[i]);
        else if(Map[X+x[i]][Y+y[i]]=='#')
            ans++;
    }
}

void read()
{
    int j,k;
    string s;
    scanf("%d",&n);
    memset(Map,'#',sizeof(Map));
    for(j=1; j<=n; j++)
    {
        cin>>s;
        for(k=1; k<=n; k++)
            Map[j][k]=s[k-1];
    }
}

void print()
{
    printf("%d\n",(ans-4)*9);
}
int main()
{
    read();
    DFS(1,1);
    if(path[n][n]==0)
        DFS(n,n);
    print();
    return 0;
}







1044. Lucky Tickets. Easy!

一张票,如果有n位,如果前一半digits和与后一般相同就是Lucky Tickets; 貌似题目给出的数据只有偶数,而且最大不超过9
如果知道位数n,那么嵌套for枚举就好了,但是不知道的情况下呢?  递归啊!  或者分类讨论啊!
思路:穷举的思路,枚举每一位的数字,其和作为下标,将改下标的数组元素++,也就是说该和的种类数又多了一种;然后前一半种类数*后一般和相同的种类数
以4为例:
num[i+j]++;     for()ans+=num[k]*num[k];
还有一种思路貌似是数学思路,比较好!  当然打表也可以! 如果仅仅是为了AC的话!
 下面是我的递归的解法!
#include<algorithm>
#include <iostream>
#include <cmath>
#include<cstring>
using namespace std;

int n;
int num[2000];
void f (int x,int ceng)
{
    for(int i=0; i<10; ++i)
    {
        if(ceng==n)  num[x+i]++;
        else f(x+i,ceng+1);
    }
}

int main()
{
    memset(num,0,sizeof(num));

    cin>>n;
    n/=2;
    f(0,1);
    int a=0;
    for(int i=0; i<=45; ++i)///最多45
        a+=(num[i]*num[i]);
    cout<<a<<endl;
    return 0;
}
 分类枚举的思路,以n=4为例:
void f4()
{///这是4位的情况!  枚举2位,后面的2位和前面的一样!
     int count=0;
     for(int i1=0; i1<=9; i1++)
       for(int i2=0; i2<=9; i2++)
            num[i1+i2]++;
    int ans=0;
    for(int i=0;i<=45;++i)
          ans+=(num[i]*num[i]);
     cout<<count<<endl;
}

下面是别人的代码(自己还需研究):
#include<iostream>
using namespace std;
int A[5][45];
int tot,n;
int main()
{
	int i,j,k;
	scanf("%d",&n);
	n/=2;
	A[0][0]=1;
	for(i=0;i<n;i++)
		for(k=0;k<=i*9;k++)
			for(j=0;j<10;j++)
				A[i+1][k+j]+=A[i][k];
	for(i=0;i<=n*9;i++)
		tot+=A[n][i]*A[n][i];
	printf("%d/n",tot);
	return 0;
}
/*
I: 2   O: 10
I: 4   O: 670
I: 6   O: 55252
I: 8   O: 4816030
*/


1047. Simple Calculations

这个题应该坚持推下去的,推导一般就放弃了,
An+1  -An =A1-A0+2*Sn;
知道An+1 知道A0,Sn,怎么消去An来求A1呢?!   
条件不够,再构造条件,另n=1,=2,=3,……=n 最后能消掉中间项,只剩下一个未知项A1 !
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int f[1010];
int n;
double a0,am,c[3010];
double sum[3010];


int main()
{
    cin>>n;
    cin>>a0>>am;
    memset(sum,0,sizeof(sum));

    for(int i=1;i<=n;++i)
     {
         cin>>c[i];
         sum[i]=sum[i-1]+c[i];
     }
    double ans=0;
    for(int i=1;i<=n;++i)
    {
        ans+=sum[i];
    }
    printf("%.2f\n",(am+n*a0-2*ans)/(n+1));///其实这里的精度控制的不是很好!
    return 0;
}



1053. Pinocchio

题目意思到现在也不知道,光看到别人的结题报告说是辗转相除法……
#include<iostream>

using namespace std;

int gcd(int a,int b)
{
    if(a%b==0)return b;
    else gcd(b,a%b);///不加return 总感觉是错的,但是还是A了……
}

int main()
{
    int n;
    cin>>n;int a[n];
    for(int i=0;i<n;++i)cin>>a[i];
    for(int i=n-1;i>0;i--)a[i-1]=gcd(a[i],a[i-1]);
    cout<<a[0]<<endl;
    return 0;
}




1068. Sum

给一个n,然后求1+2+……+n  注意正负数!


1073. Square Country

给一个数,看最少用几个平方数就把他凑出来……       将平方数打表,然后完全背包

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int k,ans;
int que[300];
int tot;
void init()
{
    tot=0;  que[0]=0;
    for(int i=1;que[tot-1]<=60000;i++)
        que[tot++]=i*i;
}
int main()
{
    init();
    int n;cin>>n;
    int dp[n];
    memset(dp,0,sizeof(dp));///注意这里初始化时0
    dp[0]=1;
    for(int i=1;i<=n;++i)
    {
        for(int j=0;j<tot;++j)
        {///完全背包的思想
            if(i>=que[j]&&dp[i-que[j]])
            {
                if(dp[i])
                {//if(i==344&&dp[i-que[j]]+1<dp[i])cout<<que[j]<<endl;;
                    dp[i]=min(dp[i-que[j]]+1,dp[i]);
                }else
                {
                    dp[i]=dp[i-que[j]]+1;///注意这里忘记加1的话……
                    //cout<<que[j]<<endl;
                }
            }
        }
    }
    cout<<dp[n]-1<<endl;///注意这里为什么减1


    return 0;
}




1079. Maximum

  • a0 = 0
  • a1 = 1
  • a2i = ai
  • a2i+1 = ai + ai+1
给出n,然后求a0~an的最值!  简单的暴力求法,不超时!   注意排序sort(num,num+n+1)啊!

1080. Map Coloring

做这个题的时候让我重新找回了openjudge上的感觉了,一个题不会,慢慢凭感觉也能改对,改着改着就对了……

用媛姐的话说:

”图的染色问题,类似二分图的染色问题。其实这题就是问你能不能类似二分图一样把图染成两个颜色,而且相邻顶点颜色不一样。

直接DFS。。。没啥好说的。如果遇到不合适的,输出-1.“

DFS遍历的过程中遇到一个点设成1,下一个点就设成0;同时记录在ans里面,随时判断相邻的点ans是否相同,相同则不合适……

注意图的链式前向星和树的链式前向星不一样……  如何避免重复!!……

#include<iostream>
#include<cstring>
using namespace std;

struct Edge
{
    int u,v;
    int val;
    int next;
}edg[100*100];
int head[100];
int tot;
void addEdge(int u,int v)
{
    edg[tot].v=v;
    edg[tot].next=head[u];
    head[u]=tot++;
}
int n;

int ans[105];
bool flag=0;
void DFS(int x,int f,int e)
{///如何避免死循环呢?!!!!     以前做过的题用不会放父节点的方式便面重复而这里不行,因为这是图,而那次是树,树和图终究还是有区别的!
    if(flag)return;
    e=(e==0?1:0);
    if(x==1)ans[x]=0;
    else ans[x]=e;
    for(int i=head[x];i!=-1;i=edg[i].next)
    {
        int v=edg[i].v;
//        if(v!=f)
//        {
            if(ans[v]==ans[x]){flag=1;return ;}///表示冲突了
            else if(ans[v]==-1)DFS(v,x,e);///???!!!!!如果不是这样就会死循环……  先超内存再超时……
            ///图的DFS就得设访问标志灯避免重复,而树则可以不必,他可以设置不回访父节点就好了
        //}
    }

}

int main()
{
    memset(head,-1,sizeof(head));
    memset(ans,-1,sizeof(ans));
    tot=0;
    cin>>n;
    for(int i=1;i<=n;++i)
    {
        int t;
        while(cin>>t&&t)
        {
            addEdge(i,t);
            addEdge(t,i);
        }
    }
    DFS(1,1,1);
    if(flag){cout<<-1<<endl;return 0;}
    for(int i=1;i<=n;++i)///加上这一for是为了避免整个图不是一个联通块来说的,但好像没有这样的测试数据!
        if(ans[i]==-1)DFS(i,i,1);
   if(flag){cout<<-1<<endl;return 0;}
    for(int i=1;i<=n;++i)cout<<ans[i];
    cout<<endl;


    return 0;
}




1082. Gaby Ivanushka

根据所给的程序,输入n个数,然后使程序什么情况下输出“Beutiful Vasilisa”!  其实程序就是快速排序,然后c就是比较次数,看什么情况下比较次数符合(c==(N*N+3*N-4)/2 
其实如果对快速排序熟悉,或者大胆猜测一下,这个肯定是某种情况下的复杂度,那么只能是有序状态下的复杂度了
代码就不附了……
不过题干中快拍的实现我还是保存一下吧!
#include <stdio.h>
using namespace std;
#define  N 10
long c;
long A[N];

long P(long l, long r)
{
    long x=A[l],
         i=l-1,
         j=r+1,t;
    while(1)
    {
        do
        {
            --j;
            ++c;
        }
        while(A[j]>x);

        do
        {
           // printf("%s\n","ok");
            ++i;
            ++c;
        }
        while(A[i]<x);

        if(i<j)
        {
            t=A[i];
            A[i]=A[j];
            A[j]=t;
        }
        else return j;
    }
}

void Q(long l, long r)
{
    long n;
    if(l<r)
    {
        n=P(l,r);
        Q(l,n);
        Q(n+1,r);
    }
}

int main(void)
{
    printf("Please cin %d number:\n",N);
    c=0;
    for(long i=0; i<N; ++i)
        scanf("%ld", &A[i]);
    Q(0,N-1);


    if(c==(N*N+3*N-4)/2)
     {
          printf
        ("Beutiful Vasilisa");
       // printf(" %d",c);
     }
    else printf
        ("Immortal Koshcei");
    return 0;
}



1083. Factorials!!!

给出n !!……! 的形式,输出n*(n-k)*(n-2k)……1/(n-n/k*k)   就是乘到再见就是负数的情况下……  代码不贴了……

1084. Goat in the Garden

  给一个正方形的草坪中,在中间有一个stake,拴着一只goat,问羊能吃到的草的最大面积是多少,保留三位小数! 

分三种情况,圆较小,全部在草坪内部; 一部分在草坪外部;或者全部把草坪包围了……

这是计算几何题,貌似是入门题!  但对于我这个计算几何菜鸟来说是一窍不通!

http://blog.csdn.net/zxy_snow/article/details/6407157   先保留小媛的解题报告!  


1086. Cryptography

求第k个素数!  素数打表
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=10000004;
int num[N];
bool isprime[N];
int prime[N];
int tot;

void init()
{
    memset(isprime,true,sizeof(isprime));
    isprime[0]=isprime[1]=false;

    for(int i=2; i<N; ++i)if(isprime[i])
    {
        prime[++tot]=i;
        for(int j=i*2;j<N;j+=i)
            isprime[j]=false;

    }

}

int main()
{
    init();
    int n;
    while(cin>>n)
    {
        int x;
        for(int i=0;i<n;++i)
        {
            cin>>x;
            cout<<prime[x]<<endl;
        }
    }

return 0;
}


1087. The Time to Take Stones

一堆石头,每人每次只能取走k[i]个! 谁取走最后一个谁就是输家!

怎么办呢?!  既然分类是game,就得有博弈中的必败态必胜态相互转移!     呵呵,既然转移很多时候就可以用到动态规划……!   

因为每个状态转化而来的路径不一样 ,因为K[i]有嘛,怎么办呢?!!   记住题意,按最优方式去取石头,如果一个人从一个状态可以转化到下一个状态,那么他想转化到的下一个状态是什么呢?! 当然是必败态了,如果能转化到的所有状态里面有一个是必败态,那就OK了,如果一个也没有,抱歉……     那么突破点在哪里呢?!  当然是最后那块石头了……  用discuss上的一句话叫做bottom-up DP;  注意边界的处理,dp[n]是必胜还是必败,设置的时候设置成取这个还是取完这个!

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

int n,m;
int k[60];
int dp[10010];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;++i)
      cin>>k[i];
    
    memset(dp,0,sizeof(dp));
    sort(k+1,k+m+1);
    for(int i=n;i>=1;--i)
    {
        for(int j=1;j<=m;++j) if(dp[i]==0&&i>=k[j])dp[i-k[j]]=1;
    }
    if(dp[1])cout<<1<<endl;
    else cout<<2<<endl;


    return 0;
}



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值