第四届福建省大学生程序设计竞赛

FZU2140  Forever 0.5

题意:构造一些点使得满足一些条件

思路:把1个点放在(0,0)然后n-1点平均分在60度的圆弧上,这样也就是有n-1对点距离为1.0

因为是60°所以n-1里面有一对点距离是1.0

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#include<queue>
#include<cstring>
#include<set>
#include<stack>
#include<string>
#include<ctime>
#define LL long long
#define maxn 50
#define MAX 700010
#define mod  1000000007
#define INF 0x3f3f3f3f
#define eps 1e-5
using namespace std;
const double pi = acos(-1.0) ;

struct point
{
    double x , y ;
    point(){};
    point( double xx , double yy )
    {
        x = xx ; y = yy ;
    }
} ;
point operator - ( point a , point b )
{
    return point( b.x-a.x , b.y-a.y ) ;
}
double cross( point a  , point b )
{
    return a.x*b.y - a.y*b.x ;
}

int cmp( point a , point b )
{
    return a.y < b.y || a.y == b.y && a.x < b.x ;
}
double get_erea(point *p,int n)
{
    double ans=0;
    for(int i = 1 ; i < n-1 ;i++)
    {
        ans += cross(p[i]-p[0],p[i+1]-p[0]) ;
    }
    return ans/2;
}
double len(point a,point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
point Rotate(point a,double rad)
{
    return point(a.x*cos(rad)-a.y*sin(rad),a.x*sin(rad)+a.y*cos(rad)) ;
}
void out(point *p,int n )
{
    for( int i = 0 ; i < n ;i++)
        printf("%.6lf %.6lf\n",p[i].x,p[i].y) ;
}
bool check(point *p,int n )
{
    int ans=0,i,j;
    for( i = 0 ; i < n ;i++)
        for( j = i+1 ; j< n;j++)
    {
        double u = len(p[i],p[j]) ;
        if(fabs(u-1.0) <eps)ans++;
        else if(u>1.0) return false;
    }
    if(ans==n) return true;
    return false;
}
int main()
{
    int T,case1=0,n ;
    int i,j,k,u,v,ans;
    point p[110],aa ;
    double rad;
    cin >> T ;
    while(T--)
    {
         scanf("%d",&n) ;
         if(n<=3)
         {
             puts("No");
             continue ;
         }
         p[0].x=0;
         p[0].y=0;
         p[1].x=1;
         p[1].y=0;
         aa=p[1] ;
         rad=pi/3;
         rad /= (n-2);
         for(i = 2 ; i < n ;i++)
         {
            aa=Rotate(aa,rad) ;
            p[i]=aa;
         }
         double ans=get_erea(p,n) ;
        // printf("%.6lf\n",ans) ;
          //   cout<<check(p,n) << endl;
         if(ans+eps>=0.5&&ans<=0.75)
         {
             puts("Yes") ;
             out(p,n) ;
         }
         else puts("No") ;
    }
    return 0;
}
View Code

FZU2141 Sub-Bipartite Graph

题意:取出原图的一个子图,这个图是二分图,有至少m/2条边

思路:设二分图的两个部分是A,B,开始所有点在B里面。每次寻找最多度数(去掉A里面的点)的一个点放到A里面,然后判断是否满足要求

没有就继续上面过程。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#include<queue>
#include<cstring>
#include<set>
#include<stack>
#include<string>
#include<ctime>
#define LL long long
#define u64 unsigned long long
#define maxn 100010
#define mod  1000000007
#define INF 1000010
#define eps 1e-5
using namespace std;

vector<int>qe[110],ans ;
int in[110];
bool vi[110] ,hehe[110];

int find(int n)
{
    memset(in,0,sizeof(in)) ;
    for(int i = 1 ; i <= n ;i++)if(!hehe[i])
    {
        for(int j = 0 ; j < qe[i].size();j++)
        {
            int v = qe[i][j] ;
            if(!hehe[v])in[i]++ ;
        }
    }
    int id=INF,Max=0;
    for( int i = 1 ; i <= n;i++)if(!hehe[i])
    {
        if(Max<in[i])
        {
            Max=in[i] ;
            id=i;
        }
    }
    return id;
}
bool check(int n,int m)
{
    int cnt=0;
    for(int i = 1 ; i <= n ;i++)if(hehe[i])
    {
        for( int j = 0 ; j < qe[i].size();j++)
        {
            int v = qe[i][j] ;
            if(!hehe[v])cnt++;
        }
    }
    if(cnt>=m/2) return true;
    return false;
}
int main()
{
    int i,j,n,m,k;
    int u,v,len ,cnt ;
    int T;
    cin >> T ;
    while(T--)
    {
        scanf("%d%d",&n,&m) ;
        ans.clear();
        for( i = 1 ; i <= n ;i++)
        {
            hehe[i]=false;
            qe[i].clear();
        }
        for( i = 1 ; i <= m;i++)
        {
            scanf("%d%d",&u,&v) ;
            qe[u].push_back(v) ;
            qe[v].push_back(u) ;
        }
        len=0;
        while(true)
        {
            k=find(n) ;
            if(k==INF) break ;
            hehe[k]=true;
            ans.push_back(k) ;
            if(check(n,m)) break ;
        }
        cout << ans.size() ;
        for( i = 0 ; i < ans.size();i++)
            cout << " " << ans[i] ;
        puts("") ;
        cout << n-ans.size() ;
        for( i = 1 ; i <= n ;i++)
        {
            if(!hehe[i]){
                    printf(" %d",i) ;
            }
        }
        puts("") ;
    }
    return 0 ;
}
View Code

FZU2143 Board Game

题意:不好说

思路:棋盘模型,用最小费用流做。

(a-b)^2 = a*a-2*a*b+b*b,b是常数,忽略。

对于偶数的格子,源点连向它k条边,第i条边的流量为1,花费是 i-1增加到i的代价

也就是 2*i-1-2*b

对于奇数的格子也是一样,不过是它连向汇点

然后奇数格子向相邻的格子连边,代价 0,流量INF

在跑最小费用流的时候,如果dis[t] >= 0 那么就可以退出了,

因为如果加上,那么总的代价就会增加

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#include<queue>
#include<cstring>
#include<set>
#include<stack>
#include<string>
#include<ctime>
#define LL long long
#define u64 unsigned long long
#define maxn 100010
#define mod  1000000007
#define INF 1000010
#define eps 1e-5
using namespace std;

struct node
{
    int u,v,cap,flow,cost,next;
}edge[maxn];
int inf=INF,dis[1010],pre[10010],top;
int head[10010];
bool vis[10010] ;

void add(int u,int v,int cap,int flow,int cost)
{
    edge[top].u = u ;
    edge[top].v = v ;
    edge[top].cap = cap ;
    edge[top].flow = flow ;
    edge[top].cost = cost ;
    edge[top].next = head[u] ;
    head[u]=top++ ;
}
void Unit(int u,int v,int cap,int cost )
{
    //cout<<u<<" "<<v<<endl;
    add(u,v,cap,0,cost);
    add(v,u,0,0,-cost) ;
}
void init()
{
    memset(head,-1,sizeof(head)) ;
    top=0;
}
bool spfa(int s, int t)
{
    queue<int>q;
    // why
    for (int i = 0; i < 1010; i++)
    {
        dis[i] = inf;
        vis[i] = false;
        pre[i] = -1;
    }
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].v;
            if (edge[i].cap > edge[i].flow &&
                    dis[v] > dis[u] + edge[i].cost )
            {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if (!vis[v])
                {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(dis[t]>=0) return false;////////././.?
    if (pre[t] == -1)return false;
    else return true;
}
int minCost(int s, int t)
{
    int flow = 0;
    int cost = 0;
    while (spfa(s, t))
    {
        int Min = inf;
        for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].v])
        {
            if (Min > edge[i].cap - edge[i].flow)
                Min = edge[i].cap - edge[i].flow;
        }
        for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].v])
        {
            edge[i].flow += Min;
            edge[i ^ 1].flow -= Min;
            cost += edge[i].cost * Min;
        }
        flow += Min;
    }
   // cout<<cost<<"========"<<endl;
    return cost;
}

int mat[10][10] ,map1[10][10];
int main()
{
    int n,m,i,j,k,ans,s,e;
    int T,case1=0,cnt;
    cin >> T ;
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k) ;
        init();
        ans=cnt=0;
        for( i = 0 ;i < n ;i++)
            for( j = 0 ; j < m ;j++){
             scanf("%d",&mat[i][j]) ;
             ans += mat[i][j]*mat[i][j] ;
             map1[i][j]=++cnt ;
        }
        s = 0 ;
        e = ++cnt ;
        for( i = 0 ; i < n ;i++)
            for( j = 0 ; j < m ;j++)
        {
            for( int u = 1 ; u <= k ;u++)
            {
               if(i%2==j%2) Unit(s,map1[i][j],1,2*u-1-2*mat[i][j]) ;
               else  Unit(map1[i][j],e,1,2*u-1-2*mat[i][j]) ;
            }
            if(i%2==j%2)
            {
                if(i<n-1)Unit(map1[i][j],map1[i+1][j],INF,0) ;
                if(j<m-1)Unit(map1[i][j],map1[i][j+1],INF,0) ;
                if(i>=1)Unit(map1[i][j],map1[i-1][j],INF,0) ;
                if(j>=1)Unit(map1[i][j],map1[i][j-1],INF,0) ;
            }
        }
        printf("Case %d: %d\n",++case1,ans+minCost(s,e)) ;
    }
    return 0 ;
}
View Code

FZU 2144 Shooting Game

题意:有n个物体,它出现在一些地方,有一个飞行的方向,速度为1;一个人可以一次袭击把圆里面的所以物体击落

思路:问最少多少次袭击,可以把最多的物品击落。

联立球的方程和直线方程,可以解出 进去时间和出来时间,也就是一个居间

这样就是用最少的点覆盖所有线段问题

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#include<queue>
#include<cstring>
#include<set>
#include<stack>
#include<string>
#include<ctime>
#define LL long long
#define u64 unsigned long long
#define maxn 100010
#define mod  1000000007
#define INF 0x3f3f3f3f
#define eps 1e-6
using namespace std;

struct node
{
    double L,R ;
    bool operator<(const node&s) const
    {
        return R < s.R ;
    }
}qe[maxn] ;

void getnumq(int& x){
    char ch=getchar();
    bool mk=0;
    x = 0;
    while(ch<48 || ch>57){
        if(ch=='-')    mk=1;
        ch=getchar();
    }
    while(ch>=48 && ch<=57){
        x = x*10+ch-48;
        ch = getchar();
    }
    if(mk)    x=-x;
}
void getnum(double& x){
    int y;
    getnumq(y);
    x = y*1.0;
}
int solve(int m)
{
    sort(qe,qe+m) ;
    int ans=0,j;
    for( int i = 0 ; i < m ;i++)
    {
        ans++ ;
        j=i+1;
        while(j<m && qe[j].L <=qe[i].R )
        {
            j++ ;
        }
        i = j-1;
    }
    return ans;
}
int main()
{
    int i,n,m,j,len,T,case1=0;
    double ax,ay,az,dx,dy,dz ;
    double r,a,b,c,d;
    cin >> T ;
    while(T--)
    {
        scanf("%d%lf",&n,&r) ;
        len=0;
        for( j = 1 ; j <= n ;j++ )
        {
            getnum(ax); getnum(ay); getnum(az);
            getnum(dx); getnum(dy); getnum(dz);
            a = dx*dx+dy*dy+dz*dz;
            b = 2*(ax*dx+ay*dy+az*dz);
            c = ax*ax+ay*ay+az*az-r*r;
            d = b*b-4*a*c;
            if(d<0)continue;
            qe[len].L = (b*(-1)-sqrt(d))/(a*2);
            qe[len].R = (b*(-1)+sqrt(d))/(a*2);
            if(qe[len].L<0 && qe[len].R<0)continue;
            len++;
        }
        printf("Case %d: %d %d\n",++case1,len,solve(len)) ;
    }
    return 0 ;
}
View Code

FZU 2148 Moon Game

题意:给出n个点,问组成凸四边形的组数

思路:数据很小,枚举。然后判断是否是凸的,

判断大概可以用凸包了......

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#include<queue>
#include<cstring>
#include<set>
#include<stack>
#include<string>
#include<ctime>
#define LL long long
#define maxn 50
#define MAX 700010
#define mod  1000000007
#define INF 0x3f3f3f3f
#define eps 1e-6
using namespace std;

struct point
{
    int x , y ;
    point(){};
    point( int xx , int yy )
    {
        x = xx ; y = yy ;
    }
} ;
point qe[maxn] , p[maxn] ;
point operator - ( point a , point b )
{
    return point( b.x-a.x , b.y-a.y ) ;
}
int cross( point a  , point b )
{
    return a.x*b.y - a.y*b.x ;
}

int cmp( point a , point b )
{
    return a.y < b.y || a.y == b.y && a.x < b.x ;
}
int Get( int n )
{
    sort(qe,qe+n,cmp) ;
    int m = 1 , i , k;
    if( n == 0 ) return 0 ;p[0] = qe[0] ;
    if( n == 1 ) return 1 ;p[1] = qe[1] ;
    if( n == 2 ) return 2 ;p[2] = qe[2] ;
    for( i = 2 ; i < n ;i++)
    {
        while( m && cross( p[m]-p[m-1] , qe[i] - p[m-1] ) <= 0 )m-- ;
        p[++m] = qe[i] ;
    }
    k = m ;p[++m] = qe[n-2] ;
    for( i = n-3 ; i >= 0 ; i-- )
    {
        while( m > k && cross( p[m]-p[m-1] , qe[i]-p[m-1] ) <= 0 )m-- ;
        p[++m] = qe[i] ;
    }
    return m ;
}
int xx[maxn],yy[maxn] ;
int main()
{
    int T,case1=0,n ;
    int i,j,k,u,v,ans;
    cin >> T ;
    while(T--)
    {
         scanf("%d",&n) ;
         for( i = 1 ; i <= n ;i++)
            scanf("%d%d",&xx[i],&yy[i]) ;
         ans=0;
         for( i=1 ; i<= n ;i++)
           for(j=i+1;j<=n;j++)
             for(u=j+1;u<=n;u++)
              for(v=u+1;v<=n;v++)
         {
             qe[0].x=xx[i] ;qe[0].y=yy[i] ;
             qe[1].x=xx[j] ;qe[1].y=yy[j] ;
             qe[2].x=xx[u] ;qe[2].y=yy[u] ;
             qe[3].x=xx[v] ;qe[3].y=yy[v] ;
             k = Get(4) ;
             //cout<<k<<endl;
             if(k==4)ans++;
         }
         printf("Case %d: %d\n",++case1,ans) ;
    }
    return 0;
}
View Code

FZU 2150 Fire Game

题意:、

思路:枚举两点,然后dfs;

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#include<queue>
#include<cstring>
#include<set>
#include<stack>
#include<string>
#include<ctime>
#define LL long long
#define maxn 20
#define MAX 700010
#define mod  1000000007
#define INF 0x3f3f3f3f
#define eps 1e-6
using namespace std;

char mat[maxn][maxn] ;
int time1[maxn][maxn] ;
int n,m;

void dfs(int x,int y,int t)
{
    time1[x][y]= t ;
    if(x>1&&mat[x-1][y]=='#'&&time1[x-1][y]>t+1)
    {
        dfs(x-1,y,t+1);
    }
    if(y>1&&mat[x][y-1]=='#'&&time1[x][y-1]>t+1)
    {
        dfs(x,y-1,t+1);
    }
    if(x<n&&mat[x+1][y]=='#'&&time1[x+1][y]>t+1)
    {
        dfs(x+1,y,t+1);
    }
    if(y<m&&mat[x][y+1]=='#'&&time1[x][y+1]>t+1)
    {
        dfs(x,y+1,t+1);
    }
}
int solve(int s1,int e1,int s2,int e2)
{

    memset(time1,INF,sizeof(time1)) ;
    dfs(s1,e1,0) ;
    dfs(s2,e2,0) ;
    int ans=-1 ;
    for(int i = 1 ; i <= n ;i++)
        for(int j = 1 ; j <= m ;j++)if(mat[i][j]=='#')
        {
            ans=max(ans,time1[i][j]) ;
        }
        return ans;
}

int getans()
{
    int ans=INF,t;
    for(int i = 1 ; i <= n ;i++)
        for(int j = 1 ; j <= m ;j++)if(mat[i][j]=='#')
    {
        for(int k = i ; k <= n ;k++)
            for(int kk = 1 ; kk <= m ;kk++)if(mat[k][kk]=='#')
        {
            int t = solve(i,j,k,kk) ;
            if(t==-1) t = INF ;
            ans=min(ans,t) ;
        }
    }
    if(ans==INF) return -1;
    return ans;
}

int main()
{
    int T,case1=0 ;
    int i,j,k,ans;
    cin >> T ;
    while(T--)
    {
         scanf("%d%d",&n,&m) ;
         for( i = 1 ; i <= n ;i++)
            scanf("%s",mat[i]+1) ;
         printf("Case %d: %d\n",++case1,getans()) ;
    }
    return 0;
}
View Code

 

posted @ 2015-03-16 22:52  _log__  阅读(180)  评论(0编辑  收藏  举报